diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 00000000..92211a4f --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "jetbrains.resharper.globaltools": { + "version": "2022.2.3", + "commands": [ + "jb" + ] + } + } +} \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..5cb62058 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,16 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/dotnet/.devcontainer/base.Dockerfile + +# [Choice] .NET version: 6.0, 3.1, 6.0-bullseye, 3.1-bullseye, 6.0-focal, 3.1-focal +ARG VARIANT="6.0-bullseye-slim" +FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT} + +# [Choice] Node.js version: none, lts/*, 18, 16, 14 +ARG NODE_VERSION="none" +RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..54811cbd --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,62 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.241.1/containers/dotnet +{ + "name": "C# (.NET)", + "build": { + "dockerfile": "Dockerfile", + "args": { + // Update 'VARIANT' to pick a .NET Core version: 3.1, 6.0 + // Append -bullseye or -focal to pin to an OS version. + "VARIANT": "6.0-bullseye", + // Options + "NODE_VERSION": "lts/*" + } + }, + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-dotnettools.csharp", + "aaron-bond.better-comments" + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [5000, 5001], + + // [Optional] To reuse of your local HTTPS dev cert: + // + // 1. Export it locally using this command: + // * Windows PowerShell: + // dotnet dev-certs https --trust; dotnet dev-certs https -ep "$env:USERPROFILE/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" + // * macOS/Linux terminal: + // dotnet dev-certs https --trust; dotnet dev-certs https -ep "${HOME}/.aspnet/https/aspnetapp.pfx" -p "SecurePwdGoesHere" + // + // 2. Uncomment these 'remoteEnv' lines: + // "remoteEnv": { + // "ASPNETCORE_Kestrel__Certificates__Default__Password": "SecurePwdGoesHere", + // "ASPNETCORE_Kestrel__Certificates__Default__Path": "/home/vscode/.aspnet/https/aspnetapp.pfx", + // }, + // + // 3. Do one of the following depending on your scenario: + // * When using GitHub Codespaces and/or Remote - Containers: + // 1. Start the container + // 2. Drag ~/.aspnet/https/aspnetapp.pfx into the root of the file explorer + // 3. Open a terminal in VS Code and run "mkdir -p /home/vscode/.aspnet/https && mv aspnetapp.pfx /home/vscode/.aspnet/https" + // + // * If only using Remote - Containers with a local container, uncomment this line instead: + // "mounts": [ "source=${env:HOME}${env:USERPROFILE}/.aspnet/https,target=/home/vscode/.aspnet/https,type=bind" ], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "dotnet restore", + + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode", + "features": { + "fish": "latest" + } +} diff --git a/.editorconfig b/.editorconfig index f8d20ed2..1745f593 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,11 +1,52 @@ +root = true # EditorConfig is awesome: https://EditorConfig.org # top-most EditorConfig file -root = true # Don't use tabs for indentation. [*] indent_style = space + +[*.cs] + +# ReSharper properties + +# Blank lines +resharper_blank_lines_after_control_transfer_statements = 1 +resharper_blank_lines_after_multiline_statements = 1 +resharper_blank_lines_around_single_line_auto_property = 1 +resharper_blank_lines_around_single_line_local_method = 1 +resharper_blank_lines_around_single_line_property = 1 +resharper_blank_lines_before_block_statements = 1 +resharper_blank_lines_before_control_transfer_statements = 1 +resharper_blank_lines_before_multiline_statements = 1 +resharper_blank_lines_before_single_line_comment = 1 + +# Baces +resharper_braces_for_for = required +resharper_braces_for_foreach = required +resharper_braces_for_ifelse = required +resharper_braces_for_while = required +resharper_braces_redundant = false +resharper_indent_nested_fixed_stmt = true +resharper_indent_nested_foreach_stmt = true +resharper_indent_nested_for_stmt = true +resharper_indent_nested_lock_stmt = true +resharper_indent_nested_usings_stmt = true +resharper_indent_nested_while_stmt = true +resharper_max_enum_members_on_line = 1 + +resharper_csharp_force_attribute_style=separate +resharper_csharp_method_or_operator_body=expression_body +resharper_csharp_max_enum_members_on_line=1 +resharper_csharp_place_attribute_on_same_line=false +resharper_csharp_wrap_object_and_collection_initializer_style=chop_always +resharper_csharp_use_heuristics_for_body_style=true + +resharper_csharp_max_initializer_elements_on_line=0 + +# Standard properties +insert_final_newline = true # (Please don't specify an indent_size here; that has too many unintended consequences.) # Code files @@ -39,7 +80,7 @@ indent_size = 2 [*.{cs,vb}] # IDE0055: Fix formatting -dotnet_diagnostic.IDE0055.severity = warning +dotnet_diagnostic.ide0055.severity = warning # Sort using and Import directives with System.* appearing first dotnet_sort_system_directives_first = true @@ -102,7 +143,7 @@ dotnet_naming_symbols.static_fields.applicable_kinds = field dotnet_naming_symbols.static_fields.required_modifiers = static dotnet_naming_style.static_field_style.capitalization = camel_case -dotnet_naming_style.static_field_style.required_prefix = s_ +dotnet_naming_style.static_field_style.required_prefix = _ # Instance fields are camelCase and start with _ dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion @@ -143,23 +184,23 @@ dotnet_naming_symbols.all_members.applicable_kinds = * dotnet_naming_style.pascal_case_style.capitalization = pascal_case # error RS2008: Enable analyzer release tracking for the analyzer project containing rule '{0}' -dotnet_diagnostic.RS2008.severity = none +dotnet_diagnostic.rs2008.severity = none # IDE0073: File header # dotnet_diagnostic.IDE0073.severity = warning # file_header_template = # IDE0035: Remove unreachable code -dotnet_diagnostic.IDE0035.severity = warning +dotnet_diagnostic.ide0035.severity = warning # IDE0036: Order modifiers -dotnet_diagnostic.IDE0036.severity = warning +dotnet_diagnostic.ide0036.severity = warning # IDE0043: Format string contains invalid placeholder -dotnet_diagnostic.IDE0043.severity = warning +dotnet_diagnostic.ide0043.severity = warning # IDE0044: Make field readonly -dotnet_diagnostic.IDE0044.severity = warning +dotnet_diagnostic.ide0044.severity = warning # RS0016: Only enable if API files are present dotnet_public_api_analyzer.require_api_files = true @@ -236,39 +277,39 @@ csharp_preserve_single_line_statements = true [src/CodeStyle/**.{cs,vb}] # warning RS0005: Do not use generic CodeAction.Create to create CodeAction -dotnet_diagnostic.RS0005.severity = none +dotnet_diagnostic.rs0005.severity = none [src/{Analyzers,CodeStyle,Features,Workspaces,EditorFeatures,VisualStudio}/**/*.{cs,vb}] # IDE0011: Add braces csharp_prefer_braces = when_multiline:warning # NOTE: We need the below severity entry for Add Braces due to https://github.com/dotnet/roslyn/issues/44201 -dotnet_diagnostic.IDE0011.severity = warning +dotnet_diagnostic.ide0011.severity = warning # IDE0040: Add accessibility modifiers -dotnet_diagnostic.IDE0040.severity = warning +dotnet_diagnostic.ide0040.severity = warning # CONSIDER: Are IDE0051 and IDE0052 too noisy to be warnings for IDE editing scenarios? Should they be made build-only warnings? # IDE0051: Remove unused private member -dotnet_diagnostic.IDE0051.severity = warning +dotnet_diagnostic.ide0051.severity = warning # IDE0052: Remove unread private member -dotnet_diagnostic.IDE0052.severity = warning +dotnet_diagnostic.ide0052.severity = warning # IDE0059: Unnecessary assignment to a value -dotnet_diagnostic.IDE0059.severity = warning +dotnet_diagnostic.ide0059.severity = warning # IDE0060: Remove unused parameter -dotnet_diagnostic.IDE0060.severity = warning +dotnet_diagnostic.ide0060.severity = warning # CA1012: Abstract types should not have public constructors -dotnet_diagnostic.CA1012.severity = warning +dotnet_diagnostic.ca1012.severity = warning # CA1822: Make member static -dotnet_diagnostic.CA1822.severity = warning +dotnet_diagnostic.ca1822.severity = warning # Prefer "var" everywhere -dotnet_diagnostic.IDE0007.severity = warning +dotnet_diagnostic.ide0007.severity = warning csharp_style_var_for_built_in_types = true:warning csharp_style_var_when_type_is_apparent = true:warning csharp_style_var_elsewhere = true:warning @@ -277,4 +318,18 @@ csharp_style_var_elsewhere = true:warning # CA1822: Make member static # Not enforced as a build 'warning' for 'VisualStudio' layer due to large number of false positives from https://github.com/dotnet/roslyn-analyzers/issues/3857 and https://github.com/dotnet/roslyn-analyzers/issues/3858 # Additionally, there is a risk of accidentally breaking an internal API that partners rely on though IVT. -dotnet_diagnostic.CA1822.severity = suggestion \ No newline at end of file +dotnet_diagnostic.ca1822.severity = suggestion + +[*.{appxmanifest,asax,ascx,aspx,axaml,axml,build,config,cs,cshtml,csproj,css,dbml,discomap,dtd,htm,html,js,json,jsproj,jsx,lsproj,master,njsproj,nuspec,paml,proj,props,proto,razor,resjson,resw,resx,skin,StyleCop,targets,tasks,ts,tsx,vb,vbproj,xaml,xamlx,xml,xoml,xsd}] +indent_style = space +indent_size = 4 +tab_width = 4 + +[*.cs] +# IDE0130: Namespace does not match folder structure +dotnet_style_namespace_match_folder = false + +# IDE0130: Namespace does not match folder structure +dotnet_diagnostic.IDE0130.severity = silent + +resharper_check_namespace_highlighting=none diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..314766e9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +* text=auto eol=lf +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..331a95d5 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,14 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "nuget" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + commit-message: + prefix: "deps" diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 00000000..f42ffd37 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,16 @@ +categories: + - title: '🚀 Features' + labels: + - 'feature' + - 'enhancement' + - title: '🐛 Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - title: '🧰 Maintenance' + label: 'chore' +template: | + ## Changes + + $CHANGES \ No newline at end of file diff --git a/.github/scripts/check.sh b/.github/scripts/check.sh new file mode 100644 index 00000000..3006888a --- /dev/null +++ b/.github/scripts/check.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +dotnet jb inspectcode Notion.sln -f="Text" --no-build --include="**.cs" -o=".lint/CodeWarningResults.txt" + +totalLines=$(file .lint/CodeWarningResults.txt | nl | wc -l) + +if [[ "$totalLines" -gt 1 ]]; then + echo "There are few linter warnings - please fix them before running the pipeline" + cat .lint/CodeWarningResults.txt + exit 1 +fi diff --git a/.github/workflows/build-artifacts-code.yml b/.github/workflows/build-artifacts-code.yml index 6ecda1a3..9acedc7c 100644 --- a/.github/workflows/build-artifacts-code.yml +++ b/.github/workflows/build-artifacts-code.yml @@ -18,10 +18,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Setup .NET 5.0 SDK - uses: actions/setup-dotnet@v1 + - name: Setup .NET + uses: actions/setup-dotnet@v2 with: - dotnet-version: 5.0.x + dotnet-version: 6.0.x source-url: https://nuget.pkg.github.com/notion-dotnet/index.json env: NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index e11118ff..35959b57 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -12,23 +12,24 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Setup .NET - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v2 with: - dotnet-version: 5.0.x + dotnet-version: 6.0.x - name: Restore dependencies run: dotnet restore - # Format the output of dotnet format - - name: Add dotnet-format problem matcher - uses: xt0rted/dotnet-format-problem-matcher@v1 - # Install dotnet format as a global tool - - name: Install dotnet format + - name: Install dotnet tools + run: dotnet tool restore + + - name: Run Linter run: | - dotnet tool install --global dotnet-format - dotnet format --check --verbosity diagnostic + sudo chmod +x ./.github/scripts/check.sh + ./.github/scripts/check.sh + shell: bash - name: Build run: dotnet build --no-restore diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml new file mode 100644 index 00000000..55a63c2c --- /dev/null +++ b/.github/workflows/greetings.yml @@ -0,0 +1,23 @@ +name: Greetings + +on: [pull_request_target, issues] + +jobs: + greet-first-time-contributors: + name: Greet First-Time Contributors + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - name: Send greeting to first-time contributors + uses: actions/first-interaction@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + issue-message: > + 👋 Hi there! Thanks for opening your first issue in this repository. + We appreciate your contribution. A maintainer will take a look soon. + pr-message: > + 🎉 Thanks for your first pull request! + The team will review it shortly. We’re excited to have you contribute! diff --git a/.github/workflows/publish-code.yml b/.github/workflows/publish-code.yml index ac41c7a1..2073a733 100644 --- a/.github/workflows/publish-code.yml +++ b/.github/workflows/publish-code.yml @@ -12,9 +12,9 @@ jobs: - uses: actions/checkout@v2 - name: Setup .NET - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v2 with: - dotnet-version: 5.0.x + dotnet-version: 6.0.x source-url: https://api.nuget.org/v3/index.json env: NUGET_AUTH_TOKEN: ${{secrets.NUGET_API_KEY}} @@ -36,7 +36,7 @@ jobs: dotnet pack --no-restore --no-build -o PackOutputs -c Release -p:Version=${{ github.event.release.tag_name }} -p:PackageReleaseNotes="See https://github.com/notion-dotnet/notion-sdk-net/releases/tag/${{ github.event.release.tag_name }}" -p:PackageReadmeFile=README.md - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Notion.Net path: PackOutputs/* @@ -45,7 +45,7 @@ jobs: run: dotnet nuget push PackOutputs/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} - name: Upload Nuget packages as release artifacts - uses: actions/github-script@v2 + uses: actions/github-script@v7 with: github-token: ${{secrets.GITHUB_TOKEN}} script: | diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 00000000..4eed5a56 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,41 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - main + # pull_request event is required only for autolabeler + pull_request: + # Only following types are handled by the action, but one can default to all as well + types: [opened, reopened, synchronize] + # pull_request_target event is required for autolabeler to support PRs from forks + pull_request_target: + types: [opened, reopened, synchronize] + +permissions: + contents: read + +jobs: + update_release_draft: + permissions: + # write permission is required to create a github release + contents: write + # write permission is required for autolabeler + # otherwise, read permission is required at least + pull-requests: write + runs-on: ubuntu-latest + steps: + # (Optional) GitHub Enterprise requires GHE_HOST variable set + #- name: Set GHE_HOST + # run: | + # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV + + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5.21.1 + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + # with: + # config-name: my-config.yml + # disable-autolabeler: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/test-publish-code.yml b/.github/workflows/test-publish-code.yml index 2184c513..ce6974e6 100644 --- a/.github/workflows/test-publish-code.yml +++ b/.github/workflows/test-publish-code.yml @@ -15,9 +15,9 @@ jobs: - uses: actions/checkout@v2 - name: Setup .NET - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v2 with: - dotnet-version: 5.0.x + dotnet-version: 6.0.x source-url: https://api.nuget.org/v3/index.json env: NUGET_AUTH_TOKEN: ${{secrets.NUGET_API_KEY}} @@ -45,7 +45,7 @@ jobs: ls -l PackOutputs/ - name: Upload artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: Notion.Net path: PackOutputs/* diff --git a/.gitignore b/.gitignore index dfcfd56f..5072687a 100644 --- a/.gitignore +++ b/.gitignore @@ -348,3 +348,11 @@ MigrationBackup/ # Ionide (cross platform F# VS Code tools) working folder .ionide/ + +# Resharper linter +.lint/ + +# Rider +.idea/ + +.DS_Store diff --git a/Notion.sln.DotSettings b/Notion.sln.DotSettings new file mode 100644 index 00000000..833f39db --- /dev/null +++ b/Notion.sln.DotSettings @@ -0,0 +1,5 @@ + + PDF + API + JSON + URL diff --git a/README.md b/README.md index 8e12bbd8..d74a4cc6 100644 --- a/README.md +++ b/README.md @@ -6,30 +6,27 @@
-![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/notion-dotnet/notion-sdk-net) -![GitHub](https://img.shields.io/github/license/notion-dotnet/notion-sdk-net) +[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/notion-dotnet/notion-sdk-net)]() +[![GitHub](https://img.shields.io/github/license/notion-dotnet/notion-sdk-net)]() [![Build Status](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/ci-build.yml/badge.svg)](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/ci-build.yml) [![Build artifacts](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/build-artifacts-code.yml/badge.svg)](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/build-artifacts-code.yml) [![Publish Code](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/publish-code.yml/badge.svg)](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/publish-code.yml) [![CodeQL](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/notion-dotnet/notion-sdk-net/actions/workflows/codeql-analysis.yml) -![LGTM Alerts](https://img.shields.io/lgtm/alerts/github/notion-dotnet/notion-sdk-net) -![LGTM Grade](https://img.shields.io/lgtm/grade/csharp/github/notion-dotnet/notion-sdk-net) +[![GitHub last commit](https://img.shields.io/github/last-commit/notion-dotnet/notion-sdk-net)]() +[![GitHub commit activity](https://img.shields.io/github/commit-activity/w/notion-dotnet/notion-sdk-net)]() +[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/notion-dotnet/notion-sdk-net)]() +[![GitHub commit activity](https://img.shields.io/github/commit-activity/y/notion-dotnet/notion-sdk-net)]() -![GitHub last commit](https://img.shields.io/github/last-commit/notion-dotnet/notion-sdk-net) -![GitHub commit activity](https://img.shields.io/github/commit-activity/w/notion-dotnet/notion-sdk-net) -![GitHub commit activity](https://img.shields.io/github/commit-activity/m/notion-dotnet/notion-sdk-net) -![GitHub commit activity](https://img.shields.io/github/commit-activity/y/notion-dotnet/notion-sdk-net) - -![GitHub repo size](https://img.shields.io/github/repo-size/notion-dotnet/notion-sdk-net) -![Lines of code](https://img.shields.io/tokei/lines/github/notion-dotnet/notion-sdk-net) +[![GitHub repo size](https://img.shields.io/github/repo-size/notion-dotnet/notion-sdk-net)]() +[![Lines of code](https://img.shields.io/tokei/lines/github/notion-dotnet/notion-sdk-net)]() Provides the following packages: | Package | Downloads | Nuget | |---|---|---| -| Notion.Net | ![Nuget](https://img.shields.io/nuget/dt/Notion.Net?color=success) | ![Nuget](https://img.shields.io/nuget/v/Notion.Net) ![Nuget (with prereleases)](https://img.shields.io/nuget/vpre/Notion.Net) | +| Notion.Net | [![Nuget](https://img.shields.io/nuget/dt/Notion.Net?color=success)](https://www.nuget.org/packages/Notion.Net) | [![Nuget](https://img.shields.io/nuget/v/Notion.Net) ![Nuget (with prereleases)](https://img.shields.io/nuget/vpre/Notion.Net)](https://www.nuget.org/packages/Notion.Net) | @@ -41,9 +38,13 @@ Provides the following packages: dotnet add package Notion.Net ``` -> Note: From Nuget 2.0.0 notion client sdk default sets the Notion-Version header to 2021-08-16. - - +**Note:** default Notion-Version used by NuGet package versions +| Package version | Notion-Version | +| --- | --- | +| 4.0.0-preview-1.8.21.2022 | 2022-06-28 | +| 3.0.0+ | 2022-02-22 | +| 2.0.0+ | 2021-08-16 | +| 1.0.0+ | 2021-05-13 | ## Usage @@ -70,7 +71,7 @@ Library also provides extension method to register NotionClient with Microsoft d ``` services.AddNotionClient(options => { - AuthToken = "" + options.AuthToken = ""; }); ``` @@ -113,7 +114,6 @@ var complexFiler = new CompoundFilter( - [x] Create a database - [x] Update database - [x] Retrieve a database - - [x] List databases (Deprecated: use Search API instead) - [x] Pages - [x] Retrieve a page - [x] Create a page @@ -125,6 +125,9 @@ var complexFiler = new CompoundFilter( - [x] Retrieve block children - [x] Append block children - [x] Delete a block +- [x] Comments + - [x] Retrieve comments + - [x] Create comment - [x] Users - [x] Retrieve a User - [x] List all users diff --git a/Src/Notion.Client/Api/ApiEndpoints.cs b/Src/Notion.Client/Api/ApiEndpoints.cs index f9a9549b..86d703b3 100644 --- a/Src/Notion.Client/Api/ApiEndpoints.cs +++ b/Src/Notion.Client/Api/ApiEndpoints.cs @@ -1,65 +1,141 @@ -using System; - -namespace Notion.Client +namespace Notion.Client { public static class ApiEndpoints { public static class DatabasesApiUrls { - public static string Retrieve(string databaseId) => $"/v1/databases/{databaseId}"; - public static string List() => "/v1/databases"; - public static string Query(string databaseId) => $"/v1/databases/{databaseId}/query"; public static string Create => "/v1/databases"; - public static string Update(string databaseId) => $"/v1/databases/{databaseId}"; + + public static string Retrieve(string databaseId) + { + return $"/v1/databases/{databaseId}"; + } + + public static string Query(string databaseId) + { + return $"/v1/databases/{databaseId}/query"; + } + + public static string Update(string databaseId) + { + return $"/v1/databases/{databaseId}"; + } } public static class UsersApiUrls { - public static string Retrieve(string userId) => $"/v1/users/{userId}"; - public static string List() => "/v1/users"; + public static string Retrieve(string userId) + { + return $"/v1/users/{userId}"; + } + + public static string List() + { + return "/v1/users"; + } /// - /// Get the for retrieve your token's bot user. + /// Get the Uri for retrieve your token's bot user. /// - /// Returns a retrieve your token's bot user. - public static string Me() => "/v1/users/me"; + /// Returns a Uri retrieve your token's bot user. + public static string Me() + { + return "/v1/users/me"; + } } public static class BlocksApiUrls { - public static string Retrieve(string blockId) => $"/v1/blocks/{blockId}"; - public static string Update(string blockId) => $"/v1/blocks/{blockId}"; + public static string Retrieve(string blockId) + { + return $"/v1/blocks/{blockId}"; + } + + public static string Update(string blockId) + { + return $"/v1/blocks/{blockId}"; + } /// - /// Get the for deleting a block. + /// Get the Uri for deleting a block. /// /// Identifier for a Notion block - /// Returns a for deleting a block. - public static string Delete(string blockId) => $"/v1/blocks/{blockId}"; + /// Returns a Uri for deleting a block. + public static string Delete(string blockId) + { + return $"/v1/blocks/{blockId}"; + } + + public static string RetrieveChildren(string blockId) + { + return $"/v1/blocks/{blockId}/children"; + } - public static string RetrieveChildren(string blockId) => $"/v1/blocks/{blockId}/children"; - public static string AppendChildren(string blockId) => $"/v1/blocks/{blockId}/children"; + public static string AppendChildren(string blockId) + { + return $"/v1/blocks/{blockId}/children"; + } } public static class PagesApiUrls { - public static string Create() => $"/v1/pages"; - public static string Retrieve(string pageId) => $"/v1/pages/{pageId}"; - public static string Update(string pageId) => $"/v1/pages/{pageId}"; - public static string UpdateProperties(string pageId) => $"/v1/pages/{pageId}"; + public static string Create() + { + return "/v1/pages"; + } + + public static string Retrieve(string pageId) + { + return $"/v1/pages/{pageId}"; + } + + public static string Update(string pageId) + { + return $"/v1/pages/{pageId}"; + } + + public static string UpdateProperties(string pageId) + { + return $"/v1/pages/{pageId}"; + } /// - /// Get the for retrieve page property item + /// Get the Uri for retrieve page property item /// /// Identifier for a Notion Page /// Identifier for a Notion Property - /// - public static string RetrievePropertyItem(string pageId, string propertyId) => $"/v1/pages/{pageId}/properties/{propertyId}"; + /// Returns a Uri for Retrieve page property item + public static string RetrievePropertyItem(string pageId, string propertyId) + { + return $"/v1/pages/{pageId}/properties/{propertyId}"; + } } public static class SearchApiUrls { - public static string Search() => "/v1/search"; + public static string Search() + { + return "/v1/search"; + } + } + + public static class CommentsApiUrls + { + public static string Retrieve() + { + return "/v1/comments"; + } + + public static string Create() + { + return "/v1/comments"; + } + } + + public static class AuthenticationUrls + { + public static string CreateToken() => "/v1/oauth/token"; + public static string RevokeToken() => "/v1/oauth/revoke"; } } } diff --git a/Src/Notion.Client/Api/Authentication/AuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/AuthenticationClient.cs new file mode 100644 index 00000000..ab141200 --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/AuthenticationClient.cs @@ -0,0 +1,12 @@ +namespace Notion.Client +{ + public sealed partial class AuthenticationClient : IAuthenticationClient + { + private readonly IRestClient _client; + + public AuthenticationClient(IRestClient restClient) + { + _client = restClient; + } + } +} diff --git a/Src/Notion.Client/Api/Authentication/CreateToken/AuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/CreateToken/AuthenticationClient.cs new file mode 100644 index 00000000..23632632 --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/CreateToken/AuthenticationClient.cs @@ -0,0 +1,21 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public sealed partial class AuthenticationClient + { + public async Task CreateTokenAsync( + CreateTokenRequest createTokenRequest, + CancellationToken cancellationToken = default) + { + var body = (ICreateTokenBodyParameters)createTokenRequest; + + return await _client.PostAsync( + ApiEndpoints.AuthenticationUrls.CreateToken(), + body, + cancellationToken: cancellationToken + ); + } + } +} diff --git a/Src/Notion.Client/Api/Authentication/CreateToken/Request/CreateTokenRequest.cs b/Src/Notion.Client/Api/Authentication/CreateToken/Request/CreateTokenRequest.cs new file mode 100644 index 00000000..3fe8ffde --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/CreateToken/Request/CreateTokenRequest.cs @@ -0,0 +1,13 @@ +namespace Notion.Client +{ + public class CreateTokenRequest : ICreateTokenBodyParameters + { + public string GrantType => "authorization_code"; + + public string Code { get; set; } + + public string RedirectUri { get; set; } + + public ExternalAccount ExternalAccount { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Authentication/CreateToken/Request/ExternalAccount.cs b/Src/Notion.Client/Api/Authentication/CreateToken/Request/ExternalAccount.cs new file mode 100644 index 00000000..432d709f --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/CreateToken/Request/ExternalAccount.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + /// + /// External account info + /// + public class ExternalAccount + { + /// + /// External account key + /// + [JsonProperty("key")] + public string Key { get; set; } + + /// + /// External account name + /// + [JsonProperty("name")] + public string Name { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Authentication/CreateToken/Request/ICreateTokenBodyParameters.cs b/Src/Notion.Client/Api/Authentication/CreateToken/Request/ICreateTokenBodyParameters.cs new file mode 100644 index 00000000..e896e71a --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/CreateToken/Request/ICreateTokenBodyParameters.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public interface ICreateTokenBodyParameters + { + /// + /// A constant string: "authorization_code". + /// + [JsonProperty("grant_type")] + string GrantType { get; } + + /// + /// A unique random code that Notion generates to authenticate with your service, + /// generated when a user initiates the OAuth flow. + /// + [JsonProperty("code")] + string Code { get; set; } + + /// + /// The "redirect_uri" that was provided in the OAuth Domain & URI section + /// of the integration's Authorization settings. Do not include this field if a + /// "redirect_uri" query param was not included in the Authorization URL + /// provided to users. In most cases, this field is required. + /// + [JsonProperty("redirect_uri")] + string RedirectUri { get; set; } + + /// + /// External account details + /// + [JsonProperty("external_account")] + ExternalAccount ExternalAccount { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Authentication/CreateToken/Response/CreateTokenResponse.cs b/Src/Notion.Client/Api/Authentication/CreateToken/Response/CreateTokenResponse.cs new file mode 100644 index 00000000..fbbe7a7a --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/CreateToken/Response/CreateTokenResponse.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class CreateTokenResponse + { + [JsonProperty("access_token")] + public string AccessToken { get; set; } + + [JsonProperty("token_type")] + public string TokenType { get; set; } = "bearer"; + + [JsonProperty("bot_id")] + public string BotId { get; set; } + + [JsonProperty("duplicated_template_id")] + public string DuplicatedTemplateId { get; set; } + + [JsonProperty("owner")] + public IBotOwner Owner { get; set; } + + [JsonProperty("workspace_icon")] + public string WorkspaceIcon { get; set; } + + [JsonProperty("workspace_id")] + public string WorkspaceId { get; set; } + + [JsonProperty("workspace_name")] + public string WorkspaceName { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs new file mode 100644 index 00000000..1973638f --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/IAuthenticationClient.cs @@ -0,0 +1,33 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + /// + /// Authentication client + /// + public interface IAuthenticationClient + { + /// + /// Creates an access token that a third-party service can use to authenticate with Notion. + /// + /// + /// + /// + Task CreateTokenAsync( + CreateTokenRequest createTokenRequest, + CancellationToken cancellationToken = default + ); + + /// + /// Revokes an access token. + /// + /// + /// + /// + Task RevokeTokenAsync( + RevokeTokenRequest revokeTokenRequest, + CancellationToken cancellationToken = default + ); + } +} diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs new file mode 100644 index 00000000..c1f3ab9d --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/AuthenticationClient.cs @@ -0,0 +1,21 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public sealed partial class AuthenticationClient + { + public async Task RevokeTokenAsync( + RevokeTokenRequest revokeTokenRequest, + CancellationToken cancellationToken = default) + { + var body = (IRevokeTokenBodyParameters)revokeTokenRequest; + + await _client.PostAsync( + ApiEndpoints.AuthenticationUrls.RevokeToken(), + body, + cancellationToken: cancellationToken + ); + } + } +} diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/Request/IRevokeTokenBodyParameters.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/Request/IRevokeTokenBodyParameters.cs new file mode 100644 index 00000000..0a194516 --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/Request/IRevokeTokenBodyParameters.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public interface IRevokeTokenBodyParameters + { + /// + /// The token to be revoked. + /// + [JsonProperty("token")] + string Token { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/Request/RevokeTokenRequest.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/Request/RevokeTokenRequest.cs new file mode 100644 index 00000000..7e92ca01 --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/Request/RevokeTokenRequest.cs @@ -0,0 +1,7 @@ +namespace Notion.Client +{ + public class RevokeTokenRequest : IRevokeTokenBodyParameters + { + public string Token { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Authentication/RevokeToken/Response/RevokeTokenResponse.cs b/Src/Notion.Client/Api/Authentication/RevokeToken/Response/RevokeTokenResponse.cs new file mode 100644 index 00000000..428dd287 --- /dev/null +++ b/Src/Notion.Client/Api/Authentication/RevokeToken/Response/RevokeTokenResponse.cs @@ -0,0 +1,6 @@ +namespace Notion.Client +{ + internal class RevokeTokenResponse + { + } +} diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/BlocksClient.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/BlocksClient.cs new file mode 100644 index 00000000..50ce3c61 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/BlocksClient.cs @@ -0,0 +1,25 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public sealed partial class BlocksClient + { + public async Task AppendChildrenAsync( + BlockAppendChildrenRequest request, + CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(request.BlockId)) + { + throw new ArgumentNullException(nameof(request.BlockId)); + } + + var url = ApiEndpoints.BlocksApiUrls.AppendChildren(request.BlockId); + + var body = new BlockAppendChildrenBodyParameters(request); + + return await _client.PatchAsync(url, body, cancellationToken: cancellationToken); + } + } +} diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/Request/BlockAppendChildrenRequest.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/BlockAppendChildrenRequest.cs new file mode 100644 index 00000000..8a5a9cde --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/BlockAppendChildrenRequest.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Notion.Client +{ + public class BlockAppendChildrenRequest : IBlockAppendChildrenBodyParameters, IBlockAppendChildrenPathParameters + { + public IEnumerable Children { get; set; } + + public string After { get; set; } + + public string BlockId { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs new file mode 100644 index 00000000..ea6cef37 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenBodyParameters.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public interface IBlockAppendChildrenBodyParameters + { + [JsonProperty("children")] + IEnumerable Children { get; set; } + + /// + /// The ID of the existing block that the new block should be appended after. + /// + [JsonProperty("after")] + public string After { get; set; } + } + + internal class BlockAppendChildrenBodyParameters : IBlockAppendChildrenBodyParameters + { + public IEnumerable Children { get; set; } + + public string After { get; set; } + + public BlockAppendChildrenBodyParameters(BlockAppendChildrenRequest request) + { + Children = request.Children; + After = request.After; + } + } +} diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenPathParameters.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenPathParameters.cs new file mode 100644 index 00000000..350382f6 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/Request/IBlockAppendChildrenPathParameters.cs @@ -0,0 +1,7 @@ +namespace Notion.Client +{ + public interface IBlockAppendChildrenPathParameters + { + public string BlockId { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/AppendChildren/Response/AppendChildrenResponse.cs b/Src/Notion.Client/Api/Blocks/AppendChildren/Response/AppendChildrenResponse.cs new file mode 100644 index 00000000..88e04ad5 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/AppendChildren/Response/AppendChildrenResponse.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class AppendChildrenResponse : PaginatedList + { + [JsonProperty("block")] + public Dictionary Block { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/BlocksClient.cs b/Src/Notion.Client/Api/Blocks/BlocksClient.cs index cc06642e..92713a78 100644 --- a/Src/Notion.Client/Api/Blocks/BlocksClient.cs +++ b/Src/Notion.Client/Api/Blocks/BlocksClient.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using static Notion.Client.ApiEndpoints; namespace Notion.Client { - public class BlocksClient : IBlocksClient + public sealed partial class BlocksClient : IBlocksClient { private readonly IRestClient _client; @@ -14,41 +15,7 @@ public BlocksClient(IRestClient client) _client = client; } - public async Task> RetrieveChildrenAsync(string blockId, BlocksRetrieveChildrenParameters parameters = null) - { - if (string.IsNullOrWhiteSpace(blockId)) - { - throw new ArgumentNullException(nameof(blockId)); - } - - var url = BlocksApiUrls.RetrieveChildren(blockId); - - var queryParameters = (IBlocksRetrieveChildrenQueryParameters)parameters; - - var queryParams = new Dictionary() - { - { "start_cursor", queryParameters?.StartCursor?.ToString() }, - { "page_size", queryParameters?.PageSize?.ToString() } - }; - - return await _client.GetAsync>(url, queryParams); - } - - public async Task> AppendChildrenAsync(string blockId, BlocksAppendChildrenParameters parameters = null) - { - if (string.IsNullOrWhiteSpace(blockId)) - { - throw new ArgumentNullException(nameof(blockId)); - } - - var url = BlocksApiUrls.AppendChildren(blockId); - - var body = (IBlocksAppendChildrenBodyParameters)parameters; - - return await _client.PatchAsync>(url, body); - } - - public async Task RetrieveAsync(string blockId) + public async Task RetrieveAsync(string blockId, CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(blockId)) { @@ -57,10 +24,10 @@ public async Task RetrieveAsync(string blockId) var url = BlocksApiUrls.Retrieve(blockId); - return await _client.GetAsync(url); + return await _client.GetAsync(url, cancellationToken: cancellationToken); } - public async Task UpdateAsync(string blockId, IUpdateBlock updateBlock) + public async Task UpdateAsync(string blockId, IUpdateBlock updateBlock, CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(blockId)) { @@ -69,10 +36,10 @@ public async Task UpdateAsync(string blockId, IUpdateBlock updateBlock) var url = BlocksApiUrls.Update(blockId); - return await _client.PatchAsync(url, updateBlock); + return await _client.PatchAsync(url, updateBlock, cancellationToken: cancellationToken); } - public async Task DeleteAsync(string blockId) + public async Task DeleteAsync(string blockId, CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(blockId)) { @@ -81,7 +48,7 @@ public async Task DeleteAsync(string blockId) var url = BlocksApiUrls.Delete(blockId); - await _client.DeleteAsync(url); + await _client.DeleteAsync(url, cancellationToken: cancellationToken); } } } diff --git a/Src/Notion.Client/Api/Blocks/IBlocksClient.cs b/Src/Notion.Client/Api/Blocks/IBlocksClient.cs index 90ebf073..c5a64e59 100644 --- a/Src/Notion.Client/Api/Blocks/IBlocksClient.cs +++ b/Src/Notion.Client/Api/Blocks/IBlocksClient.cs @@ -1,38 +1,55 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; namespace Notion.Client { public interface IBlocksClient { /// - /// Retrieves a Block object using the ID specified. + /// Retrieves a Block object using the ID specified. /// /// /// Block - Task RetrieveAsync(string blockId); + Task RetrieveAsync(string blockId, CancellationToken cancellationToken = default); /// - /// Updates the content for the specified block_id based on the block type. + /// Updates the content for the specified block_id based on the block type. /// /// /// /// Block - Task UpdateAsync(string blockId, IUpdateBlock updateBlock); + Task UpdateAsync(string blockId, IUpdateBlock updateBlock, + CancellationToken cancellationToken = default); - Task> RetrieveChildrenAsync(string blockId, BlocksRetrieveChildrenParameters parameters = null); + /// + /// Returns a paginated array of child block objects contained in the block using the ID specified. + ///
+ /// In order to receive a complete representation of a block, you may need to recursively retrieve the + /// block children of child blocks. + ///
+ /// + /// + /// + Task RetrieveChildrenAsync( + BlockRetrieveChildrenRequest request, + CancellationToken cancellationToken = default + ); /// - /// Creates and appends new children blocks to the parent block_id specified. + /// Creates and appends new children blocks to the parent block_id specified. /// - /// Identifier for a block - /// + /// + /// /// A paginated list of newly created first level children block objects. - Task> AppendChildrenAsync(string blockId, BlocksAppendChildrenParameters parameters = null); + Task AppendChildrenAsync( + BlockAppendChildrenRequest request, + CancellationToken cancellationToken = default + ); /// - /// Sets a Block object, including page blocks, to archived: true using the ID specified. + /// Moves a Block object, including page blocks, to the trash: true using the ID specified. /// /// Identifier for a Notion block - Task DeleteAsync(string blockId); + Task DeleteAsync(string blockId, CancellationToken cancellationToken = default); } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksAppendChildrenParameters.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksAppendChildrenParameters.cs deleted file mode 100644 index e78899bb..00000000 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksAppendChildrenParameters.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace Notion.Client -{ - public class BlocksAppendChildrenParameters : IBlocksAppendChildrenBodyParameters - { - public IEnumerable Children { get; set; } - } -} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksRetrieveChildrenParameters.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksRetrieveChildrenParameters.cs deleted file mode 100644 index ddc73a91..00000000 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksRetrieveChildrenParameters.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Notion.Client -{ - public class BlocksRetrieveChildrenParameters : IBlocksRetrieveChildrenQueryParameters - { - public string StartCursor { get; set; } - public int? PageSize { get; set; } - } -} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/AudioUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/AudioUpdateBlock.cs index f341e441..0f4b1acb 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/AudioUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/AudioUpdateBlock.cs @@ -2,7 +2,7 @@ namespace Notion.Client { - public class AudioUpdateBlock : UpdateBlock, IUpdateBlock + public class AudioUpdateBlock : UpdateBlock { [JsonProperty("audio")] public IFileObjectInput Audio { get; set; } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BookmarkUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BookmarkUpdateBlock.cs index 71b0cec6..b147c587 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BookmarkUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BookmarkUpdateBlock.cs @@ -5,12 +5,12 @@ namespace Notion.Client { public class BookmarkUpdateBlock : IUpdateBlock { - public bool Archived { get; set; } - [JsonProperty("bookmark")] - public Data Bookmark { get; set; } + public Info Bookmark { get; set; } + + public bool InTrash { get; set; } - public class Data + public class Info { [JsonProperty("url")] public string Url { get; set; } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BreadcrumbUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BreadcrumbUpdateBlock.cs index 2e504dff..260eca82 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BreadcrumbUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BreadcrumbUpdateBlock.cs @@ -4,18 +4,18 @@ namespace Notion.Client { public class BreadcrumbUpdateBlock : IUpdateBlock { - public bool Archived { get; set; } + public BreadcrumbUpdateBlock() + { + Breadcrumb = new Info(); + } [JsonProperty("breadcrumb")] - public Data Breadcrumb { get; set; } + public Info Breadcrumb { get; set; } - public class Data - { - } + public bool InTrash { get; set; } - public BreadcrumbUpdateBlock() + public class Info { - Breadcrumb = new Data(); } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BulletedListItemUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BulletedListItemUpdateBlock.cs index 8ea533b3..cb1fa405 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BulletedListItemUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/BulletedListItemUpdateBlock.cs @@ -1,10 +1,17 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; namespace Notion.Client { - public class BulletedListItemUpdateBlock : UpdateBlock, IUpdateBlock + public class BulletedListItemUpdateBlock : UpdateBlock { [JsonProperty("bulleted_list_item")] - public TextContentUpdate BulletedListItem { get; set; } + public Info BulletedListItem { get; set; } + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/CalloutUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/CalloutUpdateBlock.cs index d26e59a9..f4489ff8 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/CalloutUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/CalloutUpdateBlock.cs @@ -3,15 +3,15 @@ namespace Notion.Client { - public class CalloutUpdateBlock : UpdateBlock, IUpdateBlock + public class CalloutUpdateBlock : UpdateBlock { [JsonProperty("callout")] public Info Callout { get; set; } public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } [JsonProperty("icon")] public IPageIcon Icon { get; set; } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/CodeUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/CodeUpdateBlock.cs index 1364adb0..164abb87 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/CodeUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/CodeUpdateBlock.cs @@ -3,18 +3,21 @@ namespace Notion.Client { - public class CodeUpdateBlock : UpdateBlock, IUpdateBlock + public class CodeUpdateBlock : UpdateBlock { [JsonProperty("code")] public Info Code { get; set; } public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } [JsonProperty("language")] public string Language { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/DividerUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/DividerUpdateBlock.cs index eb21ca28..62b83451 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/DividerUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/DividerUpdateBlock.cs @@ -4,18 +4,18 @@ namespace Notion.Client { public class DividerUpdateBlock : IUpdateBlock { - public bool Archived { get; set; } + public DividerUpdateBlock() + { + Divider = new Info(); + } [JsonProperty("divider")] - public Data Divider { get; set; } + public Info Divider { get; set; } - public class Data - { - } + public bool InTrash { get; set; } - public DividerUpdateBlock() + public class Info { - Divider = new Data(); } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/EmbedUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/EmbedUpdateBlock.cs new file mode 100644 index 00000000..8dba5a53 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/EmbedUpdateBlock.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class EmbedUpdateBlock : UpdateBlock + { + [JsonProperty("embed")] + public Info Embed { get; set; } + + public class Info + { + [JsonProperty("url")] + public string Url { get; set; } + } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/EquationUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/EquationUpdateBlock.cs index c0f32894..b809ba7c 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/EquationUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/EquationUpdateBlock.cs @@ -2,12 +2,12 @@ namespace Notion.Client { - public class EquationUpdateBlock : UpdateBlock, IUpdateBlock + public class EquationUpdateBlock : UpdateBlock { [JsonProperty("equation")] - public Data Equation { get; set; } + public Info Equation { get; set; } - public class Data + public class Info { [JsonProperty("expression")] public string Expression { get; set; } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/FileUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/FileUpdateBlock.cs new file mode 100644 index 00000000..10208c1a --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/FileUpdateBlock.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class FileUpdateBlock : UpdateBlock + { + [JsonProperty("file")] + public IFileObjectInput File { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingOneUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingOneUpdateBlock.cs index 31c3d968..672c8c85 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingOneUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingOneUpdateBlock.cs @@ -1,10 +1,19 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; namespace Notion.Client { - public class HeadingOneUpdateBlock : UpdateBlock, IUpdateBlock + public class HeadingOneUpdateBlock : UpdateBlock { [JsonProperty("heading_1")] - public TextContentUpdate Heading_1 { get; set; } + [SuppressMessage("ReSharper", "InconsistentNaming")] + public Info Heading_1 { get; set; } + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingThreeeUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingThreeeUpdateBlock.cs index 2f413e35..d2eb4861 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingThreeeUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingThreeeUpdateBlock.cs @@ -1,10 +1,19 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; namespace Notion.Client { - public class HeadingThreeeUpdateBlock : UpdateBlock, IUpdateBlock + public class HeadingThreeUpdateBlock : UpdateBlock { [JsonProperty("heading_3")] - public TextContentUpdate Heading_3 { get; set; } + [SuppressMessage("ReSharper", "InconsistentNaming")] + public Info Heading_3 { get; set; } + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingTwoUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingTwoUpdateBlock.cs index 9b314a03..da1f2833 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingTwoUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/HeadingTwoUpdateBlock.cs @@ -1,10 +1,19 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; namespace Notion.Client { - public class HeadingTwoUpdateBlock : UpdateBlock, IUpdateBlock + public class HeadingTwoUpdateBlock : UpdateBlock { [JsonProperty("heading_2")] - public TextContentUpdate Heading_2 { get; set; } + [SuppressMessage("ReSharper", "InconsistentNaming")] + public Info Heading_2 { get; set; } + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/IUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/IUpdateBlock.cs index ba93fe29..5b1de4be 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/IUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/IUpdateBlock.cs @@ -4,7 +4,7 @@ namespace Notion.Client { public interface IUpdateBlock { - [JsonProperty("archived")] - bool Archived { get; set; } + [JsonProperty("in_trash")] + bool InTrash { get; set; } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ImageUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ImageUpdateBlock.cs new file mode 100644 index 00000000..791198e9 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ImageUpdateBlock.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ImageUpdateBlock : UpdateBlock + { + [JsonProperty("image")] + public IFileObjectInput Image { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/LinkToPageUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/LinkToPageUpdateBlock.cs new file mode 100644 index 00000000..69e088c5 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/LinkToPageUpdateBlock.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class LinkToPageUpdateBlock : UpdateBlock + { + [JsonProperty("link_to_page")] + public IPageParentInput LinkToPage { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/NumberedListItemUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/NumberedListItemUpdateBlock.cs index 03e6f20b..11f92394 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/NumberedListItemUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/NumberedListItemUpdateBlock.cs @@ -1,10 +1,17 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; namespace Notion.Client { - public class NumberedListItemUpdateBlock : UpdateBlock, IUpdateBlock + public class NumberedListItemUpdateBlock : UpdateBlock { [JsonProperty("numbered_list_item")] - public TextContentUpdate NumberedListItem { get; set; } + public Info NumberedListItem { get; set; } + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/PDFUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/PDFUpdateBlock.cs new file mode 100644 index 00000000..c6e9ba0e --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/PDFUpdateBlock.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; + +namespace Notion.Client +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + public class PDFUpdateBlock : UpdateBlock + { + [JsonProperty("pdf")] + public IFileObjectInput PDF { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ParagraphUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ParagraphUpdateBlock.cs index 43fca06e..63aa2724 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ParagraphUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ParagraphUpdateBlock.cs @@ -1,10 +1,17 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; namespace Notion.Client { - public class ParagraphUpdateBlock : UpdateBlock, IUpdateBlock + public class ParagraphUpdateBlock : UpdateBlock { [JsonProperty("paragraph")] - public TextContentUpdate Paragraph { get; set; } + public Info Paragraph { get; set; } + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/QuoteUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/QuoteUpdateBlock.cs index 9d0405f0..9222112e 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/QuoteUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/QuoteUpdateBlock.cs @@ -3,15 +3,15 @@ namespace Notion.Client { - public class QuoteUpdateBlock : UpdateBlock, IUpdateBlock + public class QuoteUpdateBlock : UpdateBlock { [JsonProperty("quote")] public Info Quote { get; set; } public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/SyncedBlockUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/SyncedBlockUpdateBlock.cs new file mode 100644 index 00000000..e275f371 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/SyncedBlockUpdateBlock.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class SyncedBlockUpdateBlock : UpdateBlock + { + [JsonProperty("synced_block")] + public Info SyncedBlock { get; set; } + + public class Info + { + [JsonProperty("synced_from")] + public SyncedFromBlockId SyncedFrom { get; set; } + + public class SyncedFromBlockId + { + [JsonProperty("block_id")] + public string BlockId { get; set; } + } + } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableOfContentsUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableOfContentsUpdateBlock.cs index 9e742597..2e4adff9 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableOfContentsUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableOfContentsUpdateBlock.cs @@ -4,18 +4,18 @@ namespace Notion.Client { public class TableOfContentsUpdateBlock : IUpdateBlock { - public bool Archived { get; set; } + public TableOfContentsUpdateBlock() + { + TableOfContents = new Info(); + } [JsonProperty("table_of_contents")] - public Data TableOfContents { get; set; } + public Info TableOfContents { get; set; } - public class Data - { - } + public bool InTrash { get; set; } - public TableOfContentsUpdateBlock() + public class Info { - TableOfContents = new Data(); } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableRowUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableRowUpdateBlock.cs new file mode 100644 index 00000000..143f3e2d --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableRowUpdateBlock.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TableRowUpdateBlock : UpdateBlock + { + [JsonProperty("table_row")] + public Info TableRow { get; set; } + + public class Info + { + [JsonProperty("cells")] + public IEnumerable> Cells { get; set; } + } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableUpdateBlock.cs new file mode 100644 index 00000000..7c1f8046 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TableUpdateBlock.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TableUpdateBlock : UpdateBlock + { + [JsonProperty("table")] + public Info Table { get; set; } + + public class Info + { + [JsonProperty("has_column_header")] + public bool HasColumnHeader { get; set; } + + [JsonProperty("has_row_header")] + public bool HasRowHeader { get; set; } + } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TemplateUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TemplateUpdateBlock.cs new file mode 100644 index 00000000..32a393cd --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TemplateUpdateBlock.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TemplateUpdateBlock : UpdateBlock + { + [JsonProperty("template")] + public Info Template { get; set; } + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TextContentUpdate.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TextContentUpdate.cs deleted file mode 100644 index b0388162..00000000 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/TextContentUpdate.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace Notion.Client -{ - public class TextContentUpdate - { - [JsonProperty("text")] - public IEnumerable Text { get; set; } - } -} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ToDoUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ToDoUpdateBlock.cs index e5b5cd5b..9e244f44 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ToDoUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ToDoUpdateBlock.cs @@ -3,15 +3,15 @@ namespace Notion.Client { - public class ToDoUpdateBlock : UpdateBlock, IUpdateBlock + public class ToDoUpdateBlock : UpdateBlock { [JsonProperty("to_do")] public Info ToDo { get; set; } public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } [JsonProperty("checked")] public bool IsChecked { get; set; } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ToggleUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ToggleUpdateBlock.cs index 89b71ac9..3d7e8e87 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ToggleUpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/ToggleUpdateBlock.cs @@ -1,10 +1,17 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; namespace Notion.Client { - public class ToggleUpdateBlock : UpdateBlock, IUpdateBlock + public class ToggleUpdateBlock : UpdateBlock { [JsonProperty("toggle")] - public TextContentUpdate Toggle { get; set; } + public Info Toggle { get; set; } + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/UpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/UpdateBlock.cs index d0321e52..14704cb2 100644 --- a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/UpdateBlock.cs +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/UpdateBlock.cs @@ -2,6 +2,6 @@ { public abstract class UpdateBlock : IUpdateBlock { - public bool Archived { get; set; } + public bool InTrash { get; set; } } } diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/VideoUpdateBlock.cs b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/VideoUpdateBlock.cs new file mode 100644 index 00000000..a317139a --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RequestParams/BlocksUpdateParameters/UpdateBlocks/VideoUpdateBlock.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class VideoUpdateBlock : UpdateBlock + { + [JsonProperty("video")] + public IFileObjectInput Video { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/IBlocksAppendChildrenBodyParameters.cs b/Src/Notion.Client/Api/Blocks/RequestParams/IBlocksAppendChildrenBodyParameters.cs deleted file mode 100644 index bdd4d324..00000000 --- a/Src/Notion.Client/Api/Blocks/RequestParams/IBlocksAppendChildrenBodyParameters.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace Notion.Client -{ - // TODO: need an input version of Block - public interface IBlocksAppendChildrenBodyParameters - { - [JsonProperty("children")] - IEnumerable Children { get; set; } - } -} diff --git a/Src/Notion.Client/Api/Blocks/RequestParams/IBlocksRetrieveChildrenQueryParameters.cs b/Src/Notion.Client/Api/Blocks/RequestParams/IBlocksRetrieveChildrenQueryParameters.cs deleted file mode 100644 index 5c85e06a..00000000 --- a/Src/Notion.Client/Api/Blocks/RequestParams/IBlocksRetrieveChildrenQueryParameters.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Notion.Client -{ - public interface IBlocksRetrieveChildrenQueryParameters : IPaginationParameters - { - } -} diff --git a/Src/Notion.Client/Api/Blocks/RetrieveChildren/BlocksClient.cs b/Src/Notion.Client/Api/Blocks/RetrieveChildren/BlocksClient.cs new file mode 100644 index 00000000..c91f5c6d --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RetrieveChildren/BlocksClient.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public sealed partial class BlocksClient + { + public async Task RetrieveChildrenAsync( + BlockRetrieveChildrenRequest request, + CancellationToken cancellationToken = default) + { + if (string.IsNullOrWhiteSpace(request.BlockId)) + { + throw new ArgumentNullException(nameof(request.BlockId)); + } + + var url = ApiEndpoints.BlocksApiUrls.RetrieveChildren(request.BlockId); + + var queryParameters = (IBlockRetrieveChildrenQueryParameters)request; + + var queryParams = new Dictionary + { + { "start_cursor", queryParameters?.StartCursor }, + { "page_size", queryParameters?.PageSize?.ToString() } + }; + + return await _client.GetAsync( + url, + queryParams, + cancellationToken: cancellationToken + ); + } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/BlockRetrieveChildrenRequest.cs b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/BlockRetrieveChildrenRequest.cs new file mode 100644 index 00000000..c0e87f3e --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/BlockRetrieveChildrenRequest.cs @@ -0,0 +1,13 @@ +namespace Notion.Client +{ + public class BlockRetrieveChildrenRequest : + IBlockRetrieveChildrenQueryParameters, + IBlockRetrieveChildrenPathParameters + { + public string StartCursor { get; set; } + + public int? PageSize { get; set; } + + public string BlockId { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenPathParameters.cs b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenPathParameters.cs new file mode 100644 index 00000000..c23c2e90 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenPathParameters.cs @@ -0,0 +1,7 @@ +namespace Notion.Client +{ + public interface IBlockRetrieveChildrenPathParameters + { + public string BlockId { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenQueryParameters.cs b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenQueryParameters.cs new file mode 100644 index 00000000..68c7de32 --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Request/IBlockRetrieveChildrenQueryParameters.cs @@ -0,0 +1,6 @@ +namespace Notion.Client +{ + public interface IBlockRetrieveChildrenQueryParameters : IPaginationParameters + { + } +} diff --git a/Src/Notion.Client/Api/Blocks/RetrieveChildren/Response/RetrieveChildrenResponse.cs b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Response/RetrieveChildrenResponse.cs new file mode 100644 index 00000000..0b34619f --- /dev/null +++ b/Src/Notion.Client/Api/Blocks/RetrieveChildren/Response/RetrieveChildrenResponse.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class RetrieveChildrenResponse : PaginatedList + { + [JsonProperty("block")] + public Dictionary Block { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Comments/CommentsClient.cs b/Src/Notion.Client/Api/Comments/CommentsClient.cs new file mode 100644 index 00000000..51dadeff --- /dev/null +++ b/Src/Notion.Client/Api/Comments/CommentsClient.cs @@ -0,0 +1,12 @@ +namespace Notion.Client +{ + public partial class CommentsClient : ICommentsClient + { + private readonly IRestClient _client; + + public CommentsClient(IRestClient restClient) + { + _client = restClient; + } + } +} diff --git a/Src/Notion.Client/Api/Comments/Create/CommentsClient.cs b/Src/Notion.Client/Api/Comments/Create/CommentsClient.cs new file mode 100644 index 00000000..ac2244ea --- /dev/null +++ b/Src/Notion.Client/Api/Comments/Create/CommentsClient.cs @@ -0,0 +1,19 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public partial class CommentsClient + { + public async Task CreateAsync(CreateCommentParameters parameters, CancellationToken cancellationToken = default) + { + var body = (ICreateCommentsBodyParameters)parameters; + + return await _client.PostAsync( + ApiEndpoints.CommentsApiUrls.Create(), + body, + cancellationToken: cancellationToken + ); + } + } +} diff --git a/Src/Notion.Client/Api/Comments/Create/Request/CreateCommentParameters.cs b/Src/Notion.Client/Api/Comments/Create/Request/CreateCommentParameters.cs new file mode 100644 index 00000000..f9532726 --- /dev/null +++ b/Src/Notion.Client/Api/Comments/Create/Request/CreateCommentParameters.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public interface ICreateCommentsBodyParameters + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + } + + public interface ICreateDiscussionCommentBodyParameters : ICreateCommentsBodyParameters + { + [JsonProperty("discussion_id")] + public string DiscussionId { get; set; } + } + + public interface ICreatePageCommentBodyParameters : ICreateCommentsBodyParameters + { + [JsonProperty("parent")] + public ParentPageInput Parent { get; set; } + } + + public class CreateCommentParameters : ICreateDiscussionCommentBodyParameters, ICreatePageCommentBodyParameters + { + public string DiscussionId { get; set; } + + public IEnumerable RichText { get; set; } + + public ParentPageInput Parent { get; set; } + + public static CreateCommentParameters CreatePageComment( + ParentPageInput parent, + IEnumerable richText) + { + return new CreateCommentParameters + { + Parent = parent, + RichText = richText + }; + } + + public static CreateCommentParameters CreateDiscussionComment( + string discussionId, + IEnumerable richText) + { + return new CreateCommentParameters + { + DiscussionId = discussionId, + RichText = richText + }; + } + } +} diff --git a/Src/Notion.Client/Api/Comments/Create/Response/CreateCommentResponse.cs b/Src/Notion.Client/Api/Comments/Create/Response/CreateCommentResponse.cs new file mode 100644 index 00000000..0171d92d --- /dev/null +++ b/Src/Notion.Client/Api/Comments/Create/Response/CreateCommentResponse.cs @@ -0,0 +1,9 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Notion.Client +{ + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] + public class CreateCommentResponse : Comment + { + } +} diff --git a/Src/Notion.Client/Api/Comments/ICommentsClient.cs b/Src/Notion.Client/Api/Comments/ICommentsClient.cs new file mode 100644 index 00000000..7b7af943 --- /dev/null +++ b/Src/Notion.Client/Api/Comments/ICommentsClient.cs @@ -0,0 +1,12 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public interface ICommentsClient + { + Task CreateAsync(CreateCommentParameters createCommentParameters, CancellationToken cancellationToken = default); + + Task RetrieveAsync(RetrieveCommentsParameters parameters, CancellationToken cancellationToken = default); + } +} diff --git a/Src/Notion.Client/Api/Comments/Retrieve/CommentsClient.cs b/Src/Notion.Client/Api/Comments/Retrieve/CommentsClient.cs new file mode 100644 index 00000000..45c64f00 --- /dev/null +++ b/Src/Notion.Client/Api/Comments/Retrieve/CommentsClient.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public partial class CommentsClient + { + [SuppressMessage("ReSharper", "UnusedMember.Global")] + public async Task RetrieveAsync(RetrieveCommentsParameters parameters, CancellationToken cancellationToken = default) + { + var qp = (IRetrieveCommentsQueryParameters)parameters; + + var queryParams = new Dictionary + { + { "block_id", qp.BlockId }, + { "start_cursor", qp.StartCursor }, + { "page_size", qp.PageSize.ToString() } + }; + + return await _client.GetAsync( + ApiEndpoints.CommentsApiUrls.Retrieve(), + queryParams, + cancellationToken: cancellationToken + ); + } + } +} diff --git a/Src/Notion.Client/Api/Comments/Retrieve/Request/IRetrieveCommentsQueryParameters.cs b/Src/Notion.Client/Api/Comments/Retrieve/Request/IRetrieveCommentsQueryParameters.cs new file mode 100644 index 00000000..123a5844 --- /dev/null +++ b/Src/Notion.Client/Api/Comments/Retrieve/Request/IRetrieveCommentsQueryParameters.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public interface IRetrieveCommentsQueryParameters : IPaginationParameters + { + [JsonProperty("block_id")] + string BlockId { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Comments/Retrieve/Request/RetrieveCommentsParameters.cs b/Src/Notion.Client/Api/Comments/Retrieve/Request/RetrieveCommentsParameters.cs new file mode 100644 index 00000000..25d3d54d --- /dev/null +++ b/Src/Notion.Client/Api/Comments/Retrieve/Request/RetrieveCommentsParameters.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Notion.Client +{ + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] + public class RetrieveCommentsParameters : IRetrieveCommentsQueryParameters + { + public string BlockId { get; set; } + + public string StartCursor { get; set; } + + public int? PageSize { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Comments/Retrieve/Response/Comment.cs b/Src/Notion.Client/Api/Comments/Retrieve/Response/Comment.cs new file mode 100644 index 00000000..9b9ef2ae --- /dev/null +++ b/Src/Notion.Client/Api/Comments/Retrieve/Response/Comment.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class Comment : IObject + { + [JsonProperty("parent")] + public ICommentParent Parent { get; set; } + + [JsonProperty("discussion_id")] + public string DiscussionId { get; set; } + + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("created_by")] + public PartialUser CreatedBy { get; set; } + + [JsonProperty("created_time")] + public DateTime CreatedTime { get; set; } + + [JsonProperty("last_edited_time")] + public DateTime LastEditedTime { get; set; } + + public string Id { get; set; } + + public ObjectType Object => ObjectType.Comment; + } +} diff --git a/Src/Notion.Client/Api/Comments/Retrieve/Response/Comments.cs b/Src/Notion.Client/Api/Comments/Retrieve/Response/Comments.cs new file mode 100644 index 00000000..8b3b43b3 --- /dev/null +++ b/Src/Notion.Client/Api/Comments/Retrieve/Response/Comments.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class RetrieveCommentsResponse : PaginatedList + { + [JsonProperty("comment")] + public Dictionary Comment { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Comments/Retrieve/Response/ICommentParent.cs b/Src/Notion.Client/Api/Comments/Retrieve/Response/ICommentParent.cs new file mode 100644 index 00000000..9e05d019 --- /dev/null +++ b/Src/Notion.Client/Api/Comments/Retrieve/Response/ICommentParent.cs @@ -0,0 +1,12 @@ +using JsonSubTypes; +using Newtonsoft.Json; + +namespace Notion.Client +{ + [JsonConverter(typeof(JsonSubtypes), "type")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(PageParent), ParentType.PageId)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(BlockParent), ParentType.BlockId)] + public interface ICommentParent + { + } +} diff --git a/Src/Notion.Client/Api/Databases/DatabasesClient.cs b/Src/Notion.Client/Api/Databases/DatabasesClient.cs index ee546aef..a3dc9a8c 100644 --- a/Src/Notion.Client/Api/Databases/DatabasesClient.cs +++ b/Src/Notion.Client/Api/Databases/DatabasesClient.cs @@ -1,11 +1,11 @@ using System; -using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using static Notion.Client.ApiEndpoints; namespace Notion.Client { - public class DatabasesClient : IDatabasesClient + public sealed partial class DatabasesClient : IDatabasesClient { private readonly IRestClient _client; @@ -14,44 +14,28 @@ public DatabasesClient(IRestClient client) _client = client; } - public async Task RetrieveAsync(string databaseId) + public async Task RetrieveAsync(string databaseId, CancellationToken cancellationToken = default) { - return await _client.GetAsync(DatabasesApiUrls.Retrieve(databaseId)); - } - - [Obsolete("This endpoint is no longer recommended, use Search instead. This endpoint will only return explicitly shared pages, while search will also return child pages within explicitly shared pages. This endpoint's results cannot be filtered, while search can be used to match on page title.", false)] - public async Task> ListAsync(DatabasesListParameters databasesListParameters = null) - { - var databasesListQueryParmaters = (IDatabasesListQueryParmaters)databasesListParameters; - - var queryParams = new Dictionary() + if (string.IsNullOrWhiteSpace(databaseId)) { - { "start_cursor", databasesListQueryParmaters?.StartCursor }, - { "page_size", databasesListQueryParmaters?.PageSize?.ToString() } - }; - - return await _client.GetAsync>(DatabasesApiUrls.List(), queryParams); - } - - public async Task> QueryAsync(string databaseId, DatabasesQueryParameters databasesQueryParameters) - { - var body = (IDatabaseQueryBodyParameters)databasesQueryParameters; + throw new ArgumentNullException(nameof(databaseId)); + } - return await _client.PostAsync>(DatabasesApiUrls.Query(databaseId), body); + return await _client.GetAsync(DatabasesApiUrls.Retrieve(databaseId), cancellationToken: cancellationToken); } - public async Task CreateAsync(DatabasesCreateParameters databasesCreateParameters) + public async Task CreateAsync(DatabasesCreateParameters databasesCreateParameters, CancellationToken cancellationToken = default) { var body = (IDatabasesCreateBodyParameters)databasesCreateParameters; - return await _client.PostAsync(DatabasesApiUrls.Create, body); + return await _client.PostAsync(DatabasesApiUrls.Create, body, cancellationToken: cancellationToken); } - public async Task UpdateAsync(string databaseId, DatabasesUpdateParameters databasesUpdateParameters) + public async Task UpdateAsync(string databaseId, DatabasesUpdateParameters databasesUpdateParameters, CancellationToken cancellationToken = default) { var body = (IDatabasesUpdateBodyParameters)databasesUpdateParameters; - return await _client.PatchAsync(DatabasesApiUrls.Update(databaseId), body); + return await _client.PatchAsync(DatabasesApiUrls.Update(databaseId), body, cancellationToken: cancellationToken); } } } diff --git a/Src/Notion.Client/Api/Databases/IDatabasesClient.cs b/Src/Notion.Client/Api/Databases/IDatabasesClient.cs index c339e4da..98df1273 100644 --- a/Src/Notion.Client/Api/Databases/IDatabasesClient.cs +++ b/Src/Notion.Client/Api/Databases/IDatabasesClient.cs @@ -1,34 +1,48 @@ -using System; +using System.Threading; using System.Threading.Tasks; namespace Notion.Client { public interface IDatabasesClient { - Task RetrieveAsync(string databaseId); - Task> QueryAsync(string databaseId, DatabasesQueryParameters databasesQueryParameters); + /// + /// Retrieves a Database object using the ID specified. + /// + /// Identifier for a Notion database + /// + /// + /// + Task RetrieveAsync(string databaseId, CancellationToken cancellationToken = default); /// - /// List all Databases shared with the authenticated integration. + /// Gets a list of Pages contained in the database, filtered and ordered according to the + /// filter conditions and sort criteria provided in the request. The response may contain + /// fewer than page_size of results. /// - /// database list request parameters. - /// PaginatedList of databases. - [Obsolete("This endpoint is no longer recommended, use Search instead. This endpoint will only return explicitly shared pages, while search will also return child pages within explicitly shared pages. This endpoint's results cannot be filtered, while search can be used to match on page title.", false)] - Task> ListAsync(DatabasesListParameters databasesListParameters = null); + /// + /// + /// + /// + /// + Task QueryAsync(string databaseId, DatabasesQueryParameters databasesQueryParameters, CancellationToken cancellationToken = default); /// - /// Creates a database as a subpage in the specified parent page, with the specified properties schema. + /// Creates a database as a subpage in the specified parent page, with the specified properties schema. /// /// - /// Database - Task CreateAsync(DatabasesCreateParameters databasesCreateParameters); + /// + /// + /// + Task CreateAsync(DatabasesCreateParameters databasesCreateParameters, CancellationToken cancellationToken = default); /// - /// Updates an existing database as specified by the parameters. + /// Updates an existing database as specified by the parameters. /// /// /// - /// Database - Task UpdateAsync(string databaseId, DatabasesUpdateParameters databasesUpdateParameters); + /// + /// + /// + Task UpdateAsync(string databaseId, DatabasesUpdateParameters databasesUpdateParameters, CancellationToken cancellationToken = default); } } diff --git a/Src/Notion.Client/Api/Databases/Query/DatabasesClient.cs b/Src/Notion.Client/Api/Databases/Query/DatabasesClient.cs new file mode 100644 index 00000000..2d0c62be --- /dev/null +++ b/Src/Notion.Client/Api/Databases/Query/DatabasesClient.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public sealed partial class DatabasesClient + { + public async Task QueryAsync( + string databaseId, + DatabasesQueryParameters databasesQueryParameters, CancellationToken cancellationToken = default) + { + var body = (IDatabaseQueryBodyParameters)databasesQueryParameters; + var queryParameters = (IDatabaseQueryQueryParameters)databasesQueryParameters; + + var queryParams = queryParameters.FilterProperties? + .Select(x => new KeyValuePair("filter_properties", x)); + + return await _client.PostAsync( + ApiEndpoints.DatabasesApiUrls.Query(databaseId), + body, + queryParams, + cancellationToken: cancellationToken + ); + } + } +} diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesQueryParameters.cs b/Src/Notion.Client/Api/Databases/Query/Request/DatabasesQueryParameters.cs similarity index 75% rename from Src/Notion.Client/Api/Databases/RequestParams/DatabasesQueryParameters.cs rename to Src/Notion.Client/Api/Databases/Query/Request/DatabasesQueryParameters.cs index 454961a2..2c2bb030 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesQueryParameters.cs +++ b/Src/Notion.Client/Api/Databases/Query/Request/DatabasesQueryParameters.cs @@ -2,11 +2,16 @@ namespace Notion.Client { - public class DatabasesQueryParameters : IDatabaseQueryBodyParameters + public class DatabasesQueryParameters : IDatabaseQueryBodyParameters, IDatabaseQueryQueryParameters { public Filter Filter { get; set; } + public List Sorts { get; set; } + public string StartCursor { get; set; } + public int? PageSize { get; set; } + + public List FilterProperties { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/IDatabaseQueryBodyParameters.cs b/Src/Notion.Client/Api/Databases/Query/Request/IDatabaseQueryBodyParameters.cs similarity index 100% rename from Src/Notion.Client/Api/Databases/RequestParams/IDatabaseQueryBodyParameters.cs rename to Src/Notion.Client/Api/Databases/Query/Request/IDatabaseQueryBodyParameters.cs diff --git a/Src/Notion.Client/Api/Databases/Query/Request/IDatabaseQueryQueryParameters.cs b/Src/Notion.Client/Api/Databases/Query/Request/IDatabaseQueryQueryParameters.cs new file mode 100644 index 00000000..f8ba1f41 --- /dev/null +++ b/Src/Notion.Client/Api/Databases/Query/Request/IDatabaseQueryQueryParameters.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public interface IDatabaseQueryQueryParameters + { + [JsonProperty("filter_properties")] + List FilterProperties { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Databases/Query/Response/DatabaseQueryResponse.cs b/Src/Notion.Client/Api/Databases/Query/Response/DatabaseQueryResponse.cs new file mode 100644 index 00000000..5b7c9ec6 --- /dev/null +++ b/Src/Notion.Client/Api/Databases/Query/Response/DatabaseQueryResponse.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + // ReSharper disable once ClassNeverInstantiated.Global + public class DatabaseQueryResponse : PaginatedList + { + [JsonProperty("database")] + public Dictionary Database { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/DatabasesCreateParameters.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/DatabasesCreateParameters.cs index 79a984f9..9cb7086b 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/DatabasesCreateParameters.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/DatabasesCreateParameters.cs @@ -5,6 +5,12 @@ namespace Notion.Client { public class DatabasesCreateParameters : IDatabasesCreateBodyParameters, IDatabasesCreateQueryParameters { + [JsonProperty("icon")] + public IPageIcon Icon { get; set; } + + [JsonProperty("cover")] + public FileObject Cover { get; set; } + [JsonProperty("parent")] public ParentPageInput Parent { get; set; } @@ -14,10 +20,8 @@ public class DatabasesCreateParameters : IDatabasesCreateBodyParameters, IDataba [JsonProperty("title")] public List Title { get; set; } - [JsonProperty("icon")] - public IPageIcon Icon { get; set; } + public bool? IsInline { get; set; } - [JsonProperty("cover")] - public FileObject Cover { get; set; } + public List Description { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/IDatabasesCreateBodyParameters.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/IDatabasesCreateBodyParameters.cs index b0f97061..7eb252f2 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/IDatabasesCreateBodyParameters.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/IDatabasesCreateBodyParameters.cs @@ -13,5 +13,11 @@ public interface IDatabasesCreateBodyParameters [JsonProperty("title")] List Title { get; set; } + + [JsonProperty("is_inline")] + bool? IsInline { get; set; } + + [JsonProperty("description")] + List Description { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/MentionInput.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/MentionInput.cs index 83d1e699..48b34948 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/MentionInput.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/MentionInput.cs @@ -17,6 +17,6 @@ public class MentionInput public ObjectId Database { get; set; } [JsonProperty("date")] - public DatePropertyValue Date { get; set; } + public Date Date { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/MultiSelectOptionSchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/MultiSelectOptionSchema.cs index 89fdb0ab..a13d0930 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/MultiSelectOptionSchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/MultiSelectOptionSchema.cs @@ -1,5 +1,8 @@ -namespace Notion.Client +using System.Diagnostics.CodeAnalysis; + +namespace Notion.Client { + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] public class MultiSelectOptionSchema : SelectOptionSchema { } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RelationPropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RelationPropertySchema.cs index bee17690..4c278ac2 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RelationPropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RelationPropertySchema.cs @@ -1,23 +1,10 @@ -using System; -using Newtonsoft.Json; +using Newtonsoft.Json; namespace Notion.Client { public class RelationPropertySchema : IPropertySchema { [JsonProperty("relation")] - public RelationInfo Relation { get; set; } - - public class RelationInfo - { - [JsonProperty("database_id")] - public Guid DatabaseId { get; set; } - - [JsonProperty("synced_property_id")] - public string SynchedPropertyId { get; set; } - - [JsonProperty("synced_property_name")] - public string SynchedPropertyName { get; set; } - } + public RelationData Relation { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RollupConfigPropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RollupConfigPropertySchema.cs index e4065344..ac3c5220 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RollupConfigPropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/RollupConfigPropertySchema.cs @@ -1,5 +1,4 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; namespace Notion.Client { @@ -18,7 +17,6 @@ public class RollupConfigPropertySchema : IPropertySchema public string RollupPropertyId { get; set; } [JsonProperty("function")] - [JsonConverter(typeof(StringEnumConverter))] - public Function Function { get; set; } + public string Function { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/SelectOptionSchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/SelectOptionSchema.cs index c31aef9f..b7fd8d82 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/SelectOptionSchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/SelectOptionSchema.cs @@ -10,6 +10,6 @@ public class SelectOptionSchema [JsonProperty("color")] [JsonConverter(typeof(StringEnumConverter))] - public Color Color { get; set; } + public Color? Color { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/URLPropertyScheam.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/UrlPropertySchema.cs similarity index 78% rename from Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/URLPropertyScheam.cs rename to Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/UrlPropertySchema.cs index 468244d8..0860fa97 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/URLPropertyScheam.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesCreateParameters/PropertySchema/UrlPropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class URLPropertyScheam : IPropertySchema + public class UrlPropertySchema : IPropertySchema { [JsonProperty("url")] public Dictionary Url { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesListParameters.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesListParameters.cs deleted file mode 100644 index 51136e47..00000000 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesListParameters.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Notion.Client -{ - public class DatabasesListParameters : IDatabasesListQueryParmaters - { - public string StartCursor { get; set; } - public int? PageSize { get; set; } - } -} diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/DatabasesUpdateParameters.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/DatabasesUpdateParameters.cs index ff2e129e..5147a6b0 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/DatabasesUpdateParameters.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/DatabasesUpdateParameters.cs @@ -1,28 +1,21 @@ using System.Collections.Generic; -using Newtonsoft.Json; namespace Notion.Client { - public interface IDatabasesUpdateBodyParameters - { - [JsonProperty("properties")] - Dictionary Properties { get; set; } - - [JsonProperty("title")] - List Title { get; set; } - - [JsonProperty("icon")] - IPageIcon Icon { get; set; } - - [JsonProperty("cover")] - FileObject Cover { get; set; } - } - public class DatabasesUpdateParameters : IDatabasesUpdateBodyParameters { public Dictionary Properties { get; set; } + public List Title { get; set; } + public IPageIcon Icon { get; set; } + public FileObject Cover { get; set; } + + public bool InTrash { get; set; } + + public bool? IsInline { get; set; } + + public List Description { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/IDatabasesUpdateBodyParameters.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/IDatabasesUpdateBodyParameters.cs new file mode 100644 index 00000000..f37df431 --- /dev/null +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/IDatabasesUpdateBodyParameters.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public interface IDatabasesUpdateBodyParameters + { + [JsonProperty("properties")] + Dictionary Properties { get; set; } + + [JsonProperty("title")] + List Title { get; set; } + + [JsonProperty("icon")] + IPageIcon Icon { get; set; } + + [JsonProperty("cover")] + FileObject Cover { get; set; } + + [JsonProperty("in_trash")] + bool InTrash { get; set; } + + [JsonProperty("is_inline")] + bool? IsInline { get; set; } + + [JsonProperty("description")] + List Description { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CheckboxUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CheckboxUpdatePropertySchema.cs index a13f2911..a6890d6e 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CheckboxUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CheckboxUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class CheckboxUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class CheckboxUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("checkbox")] public Dictionary Checkbox { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CreatedByUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CreatedByUpdatePropertySchema.cs index 394868db..08a5cf74 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CreatedByUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CreatedByUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class CreatedByUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class CreatedByUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("created_by")] public Dictionary CreatedBy { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CreatedTimeUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CreatedTimeUpdatePropertySchema.cs index dee266f3..8089ecbb 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CreatedTimeUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/CreatedTimeUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class CreatedTimeUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class CreatedTimeUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("created_time")] public Dictionary CreatedTime { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/DateUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/DateUpdatePropertySchema.cs index 0c2b2143..65487dd7 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/DateUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/DateUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class DateUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class DateUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("date")] public Dictionary Date { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/EmailUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/EmailUpdatePropertySchema.cs index 412c9115..5905e68e 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/EmailUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/EmailUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class EmailUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class EmailUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("email")] public Dictionary Email { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/FilesUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/FilesUpdatePropertySchema.cs index c7b84218..9fb3b531 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/FilesUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/FilesUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class FilesUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class FilesUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("files")] public Dictionary Files { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/FormulaUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/FormulaUpdatePropertySchema.cs index 158a42bb..40cc398a 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/FormulaUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/FormulaUpdatePropertySchema.cs @@ -2,7 +2,7 @@ namespace Notion.Client { - public class FormulaUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class FormulaUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("formula")] public Formula Formula { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/LastEditedByUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/LastEditedByUpdatePropertySchema.cs index 939bc69d..c547538a 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/LastEditedByUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/LastEditedByUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class LastEditedByUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class LastEditedByUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("last_edited_by")] public Dictionary LastEditedBy { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/LastEditedTimeUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/LastEditedTimeUpdatePropertySchema.cs index f48ddf5a..28c9e55b 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/LastEditedTimeUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/LastEditedTimeUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class LastEditedTimeUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class LastEditedTimeUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("last_edited_time")] public Dictionary LastEditedTime { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/MultiSelectUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/MultiSelectUpdatePropertySchema.cs index a6b32ad3..0b7b1c0c 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/MultiSelectUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/MultiSelectUpdatePropertySchema.cs @@ -2,7 +2,7 @@ namespace Notion.Client { - public class MultiSelectUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class MultiSelectUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("multi_select")] public OptionWrapper MultiSelect { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/NumberUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/NumberUpdatePropertySchema.cs index e1f0b734..23f3ea3a 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/NumberUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/NumberUpdatePropertySchema.cs @@ -2,7 +2,7 @@ namespace Notion.Client { - public class NumberUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class NumberUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("number")] public Number Number { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/PeopleUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/PeopleUpdatePropertySchema.cs index 6fab728e..41a8892d 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/PeopleUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/PeopleUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class PeopleUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class PeopleUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("people")] public Dictionary People { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/PhoneNumberUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/PhoneNumberUpdatePropertySchema.cs index 7813671c..7d01f506 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/PhoneNumberUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/PhoneNumberUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class PhoneNumberUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class PhoneNumberUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("phone_number")] public Dictionary PhoneNumber { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RelationUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RelationUpdatePropertySchema.cs index 2091df91..25f65cd4 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RelationUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RelationUpdatePropertySchema.cs @@ -1,23 +1,10 @@ -using System; -using Newtonsoft.Json; +using Newtonsoft.Json; namespace Notion.Client { - public class RelationUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class RelationUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("relation")] - public RelationInfo Relation { get; set; } - - public class RelationInfo - { - [JsonProperty("database_id")] - public Guid DatabaseId { get; set; } - - [JsonProperty("synced_property_id")] - public string SynchedPropertyId { get; set; } - - [JsonProperty("synced_property_name")] - public string SynchedPropertyName { get; set; } - } + public RelationData Relation { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RichTextUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RichTextUpdatePropertySchema.cs index 3d6c5ec2..a20ca0ea 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RichTextUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RichTextUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class RichTextUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class RichTextUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("rich_text")] public Dictionary RichText { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RollupConfigUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RollupConfigUpdatePropertySchema.cs index 8d3395f6..8ee2d908 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RollupConfigUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/RollupConfigUpdatePropertySchema.cs @@ -1,9 +1,8 @@ using Newtonsoft.Json; -using Newtonsoft.Json.Converters; namespace Notion.Client { - public class RollupConfigUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class RollupConfigUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("relation_property_name")] public string RelationPropertyName { get; set; } @@ -18,7 +17,6 @@ public class RollupConfigUpdatePropertySchema : UpdatePropertySchema, IUpdatePro public string RollupPropertyId { get; set; } [JsonProperty("function")] - [JsonConverter(typeof(StringEnumConverter))] - public Function Function { get; set; } + public string Function { get; set; } } } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/SelectUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/SelectUpdatePropertySchema.cs index b833d0bc..678e452d 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/SelectUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/SelectUpdatePropertySchema.cs @@ -2,7 +2,7 @@ namespace Notion.Client { - public class SelectUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class SelectUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("select")] public OptionWrapper Select { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/TitleUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/TitleUpdatePropertySchema.cs index 1a74085c..54052dfc 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/TitleUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/TitleUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class TitleUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class TitleUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("title")] public Dictionary Title { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/URLUpdatePropertySchema.cs b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/URLUpdatePropertySchema.cs index 5ea7ac4c..f56d6507 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/URLUpdatePropertySchema.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/DatabasesUpdateParameters/PropertySchema/URLUpdatePropertySchema.cs @@ -3,7 +3,7 @@ namespace Notion.Client { - public class URLUpdatePropertySchema : UpdatePropertySchema, IUpdatePropertySchema + public class UrlUpdatePropertySchema : UpdatePropertySchema { [JsonProperty("url")] public Dictionary Url { get; set; } diff --git a/Src/Notion.Client/Api/Databases/RequestParams/Direction.cs b/Src/Notion.Client/Api/Databases/RequestParams/Direction.cs index c79d501d..059f42b3 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/Direction.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/Direction.cs @@ -1,7 +1,9 @@ -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMember.Global")] public enum Direction { [EnumMember(Value = null)] diff --git a/Src/Notion.Client/Api/Databases/RequestParams/IDatabasesListQueryParmaters.cs b/Src/Notion.Client/Api/Databases/RequestParams/IDatabasesListQueryParmaters.cs deleted file mode 100644 index 4432f421..00000000 --- a/Src/Notion.Client/Api/Databases/RequestParams/IDatabasesListQueryParmaters.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Notion.Client -{ - public interface IDatabasesListQueryParmaters : IPaginationParameters - { - } -} diff --git a/Src/Notion.Client/Api/Databases/RequestParams/Timestamp.cs b/Src/Notion.Client/Api/Databases/RequestParams/Timestamp.cs index 334b8c22..df2c69ca 100644 --- a/Src/Notion.Client/Api/Databases/RequestParams/Timestamp.cs +++ b/Src/Notion.Client/Api/Databases/RequestParams/Timestamp.cs @@ -1,7 +1,9 @@ -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMember.Global")] public enum Timestamp { [EnumMember(Value = null)] diff --git a/Src/Notion.Client/Api/Pages/IPagesClient.cs b/Src/Notion.Client/Api/Pages/IPagesClient.cs index b4fc728f..8dc8f5ce 100644 --- a/Src/Notion.Client/Api/Pages/IPagesClient.cs +++ b/Src/Notion.Client/Api/Pages/IPagesClient.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; namespace Notion.Client @@ -6,37 +7,58 @@ namespace Notion.Client public interface IPagesClient { /// - /// Creates a new page in the specified database or as a child of an existing page. - /// - /// If the parent is a database, the property values of the new page in the properties parameter must conform to the parent database's property schema. - /// - /// If the parent is a page, the only valid property is title. + /// Creates a new page in the specified database or as a child of an existing page. + /// If the parent is a database, the + /// property values of the + /// new page in the properties parameter must conform to the parent + /// database's property schema. + /// If the parent is a page, the only valid property is title. /// /// Create page parameters - /// Created page. - Task CreateAsync(PagesCreateParameters pagesCreateParameters); + /// Created object. + Task CreateAsync(PagesCreateParameters pagesCreateParameters, CancellationToken cancellationToken = default); - Task RetrieveAsync(string pageId); + /// + /// Retrieves a Page object using the ID specified. + /// + /// Identifier for a Notion page + /// + /// + /// + Task RetrieveAsync(string pageId, CancellationToken cancellationToken = default); + /// + /// Updates page property values for the specified page. + /// Note: Properties that are not set via the properties parameter will remain unchanged. + /// + /// Identifier for a Notion page + /// + /// Property values to update for this page. The keys are the names or IDs of the property + /// and the values are property values. + /// + /// Updated object Task UpdatePropertiesAsync( string pageId, - IDictionary updatedProperties - ); + IDictionary updatedProperties, CancellationToken cancellationToken = default); /// - /// Updates page property values for the specified page. - /// Properties that are not set via the properties parameter will remain unchanged. + /// Updates page property values for the specified page. + /// Properties that are not set via the properties parameter will remain unchanged. /// - /// - /// - /// Updated page. - Task UpdateAsync(string pageId, PagesUpdateParameters pagesUpdateParameters); + /// Identifier for a Notion page + /// Update property parameters + /// Updated object + Task UpdateAsync(string pageId, PagesUpdateParameters pagesUpdateParameters, CancellationToken cancellationToken = default); /// - /// Retrieves a property_item object for a given pageId and propertyId. Depending on the property type, the object returned will either be a value or a paginated list of property item values. + /// Retrieves a property_item object for a given pageId and propertyId. Depending on the property type, the object + /// returned will either be a value or a paginated list of property item values. /// - /// sdf sd - /// - Task RetrievePagePropertyItem(RetrievePropertyItemParameters retrievePropertyItemParameters); + /// Property body and query parameters + /// + /// + /// + Task RetrievePagePropertyItemAsync( + RetrievePropertyItemParameters retrievePropertyItemParameters, CancellationToken cancellationToken = default); } } diff --git a/Src/Notion.Client/Api/Pages/PagesClient.cs b/Src/Notion.Client/Api/Pages/PagesClient.cs index 5bce1904..76b3e41a 100644 --- a/Src/Notion.Client/Api/Pages/PagesClient.cs +++ b/Src/Notion.Client/Api/Pages/PagesClient.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; using System.Threading.Tasks; using static Notion.Client.ApiEndpoints; @@ -15,15 +17,16 @@ public PagesClient(IRestClient client) } /// - /// Creates a new page in the specified database or as a child of an existing page. - /// - /// If the parent is a database, the property values of the new page in the properties parameter must conform to the parent database's property schema. - /// - /// If the parent is a page, the only valid property is title. + /// Creates a new page in the specified database or as a child of an existing page. + /// If the parent is a database, the + /// property values of the + /// new page in the properties parameter must conform to the parent + /// database's property schema. + /// If the parent is a page, the only valid property is title. /// /// Create page parameters /// Created page. - public async Task CreateAsync(PagesCreateParameters pagesCreateParameters) + public async Task CreateAsync(PagesCreateParameters pagesCreateParameters, CancellationToken cancellationToken = default) { if (pagesCreateParameters is null) { @@ -42,50 +45,54 @@ public async Task CreateAsync(PagesCreateParameters pagesCreateParameters) throw new ArgumentNullException(nameof(bodyParameters.Properties), "Properties are required!"); } - return await _client.PostAsync(PagesApiUrls.Create(), bodyParameters); + return await _client.PostAsync(PagesApiUrls.Create(), bodyParameters, cancellationToken: cancellationToken); } - public async Task RetrieveAsync(string pageId) + public async Task RetrieveAsync(string pageId, CancellationToken cancellationToken = default) { var url = PagesApiUrls.Retrieve(pageId); - return await _client.GetAsync(url); + + return await _client.GetAsync(url, cancellationToken: cancellationToken); } - public async Task RetrievePagePropertyItem(RetrievePropertyItemParameters retrievePropertyItemParameters) + public async Task RetrievePagePropertyItemAsync( + RetrievePropertyItemParameters retrievePropertyItemParameters, CancellationToken cancellationToken = default) { var pathParameters = (IRetrievePropertyItemPathParameters)retrievePropertyItemParameters; var queryParameters = (IRetrievePropertyQueryParameters)retrievePropertyItemParameters; var url = PagesApiUrls.RetrievePropertyItem(pathParameters.PageId, pathParameters.PropertyId); - var queryParams = new Dictionary() + var queryParams = new Dictionary { { "start_cursor", queryParameters?.StartCursor }, { "page_size", queryParameters?.PageSize?.ToString() } }; - return await _client.GetAsync(url, queryParams); + return await _client.GetAsync(url, queryParams, cancellationToken: cancellationToken); } - public async Task UpdateAsync(string pageId, PagesUpdateParameters pagesUpdateParameters) + public async Task UpdateAsync(string pageId, PagesUpdateParameters pagesUpdateParameters, CancellationToken cancellationToken = default) { var url = PagesApiUrls.Update(pageId); var body = (IPagesUpdateBodyParameters)pagesUpdateParameters; - return await _client.PatchAsync(url, body); + return await _client.PatchAsync(url, body, cancellationToken: cancellationToken); } - [Obsolete("This method is obsolute. Use UpdateAsync instead. This API will be removed in future release")] + [Obsolete("This method is obsolete. Use UpdateAsync instead. This API will be removed in future release")] public async Task UpdatePropertiesAsync( string pageId, - IDictionary updatedProperties) + IDictionary updatedProperties, CancellationToken cancellationToken = default) { var url = PagesApiUrls.UpdateProperties(pageId); + var body = new UpdatePropertiesParameters { Properties = updatedProperties }; - return await _client.PatchAsync(url, body); + return await _client.PatchAsync(url, body, cancellationToken: cancellationToken); } + [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] private class UpdatePropertiesParameters { public IDictionary Properties { get; set; } diff --git a/Src/Notion.Client/Api/Pages/RequestParams/IPagesUpdateBodyParameters.cs b/Src/Notion.Client/Api/Pages/RequestParams/IPagesUpdateBodyParameters.cs index b37dd1fd..b7ea1ea6 100644 --- a/Src/Notion.Client/Api/Pages/RequestParams/IPagesUpdateBodyParameters.cs +++ b/Src/Notion.Client/Api/Pages/RequestParams/IPagesUpdateBodyParameters.cs @@ -5,8 +5,8 @@ namespace Notion.Client { public interface IPagesUpdateBodyParameters { - [JsonProperty("archived")] - bool Archived { get; set; } + [JsonProperty("in_trash")] + bool InTrash { get; set; } [JsonProperty("properties")] IDictionary Properties { get; set; } diff --git a/Src/Notion.Client/Api/Pages/RequestParams/PagesCreateParameters/PagesCreateParameters.cs b/Src/Notion.Client/Api/Pages/RequestParams/PagesCreateParameters/PagesCreateParameters.cs index 52cb0a89..08572811 100644 --- a/Src/Notion.Client/Api/Pages/RequestParams/PagesCreateParameters/PagesCreateParameters.cs +++ b/Src/Notion.Client/Api/Pages/RequestParams/PagesCreateParameters/PagesCreateParameters.cs @@ -5,9 +5,13 @@ namespace Notion.Client public class PagesCreateParameters : IPagesCreateBodyParameters, IPagesCreateQueryParameters { public IPageParentInput Parent { get; set; } + public IDictionary Properties { get; set; } + public IList Children { get; set; } + public IPageIcon Icon { get; set; } + public FileObject Cover { get; set; } } } diff --git a/Src/Notion.Client/Api/Pages/RequestParams/PagesCreateParameters/PagesCreateParametersBuilder.cs b/Src/Notion.Client/Api/Pages/RequestParams/PagesCreateParameters/PagesCreateParametersBuilder.cs index b86a207a..ed85117d 100644 --- a/Src/Notion.Client/Api/Pages/RequestParams/PagesCreateParameters/PagesCreateParametersBuilder.cs +++ b/Src/Notion.Client/Api/Pages/RequestParams/PagesCreateParameters/PagesCreateParametersBuilder.cs @@ -1,14 +1,16 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMember.Global")] public class PagesCreateParametersBuilder { - private IPageParentInput parent; - private readonly Dictionary properties = new Dictionary(); - private readonly IList children = new List(); - private IPageIcon icon; - private FileObject cover; + private readonly IList _children = new List(); + private readonly Dictionary _properties = new(); + private FileObject _cover; + private IPageIcon _icon; + private IPageParentInput _parent; private PagesCreateParametersBuilder() { @@ -16,33 +18,34 @@ private PagesCreateParametersBuilder() public static PagesCreateParametersBuilder Create(IPageParentInput parent) { - return new PagesCreateParametersBuilder - { - parent = parent - }; + return new PagesCreateParametersBuilder { _parent = parent }; } public PagesCreateParametersBuilder AddProperty(string nameOrId, PropertyValue value) { - properties[nameOrId] = value; + _properties[nameOrId] = value; + return this; } public PagesCreateParametersBuilder AddPageContent(IBlock block) { - children.Add(block); + _children.Add(block); + return this; } public PagesCreateParametersBuilder SetIcon(IPageIcon pageIcon) { - icon = pageIcon; + _icon = pageIcon; + return this; } public PagesCreateParametersBuilder SetCover(FileObject pageCover) { - cover = pageCover; + _cover = pageCover; + return this; } @@ -50,11 +53,11 @@ public PagesCreateParameters Build() { return new PagesCreateParameters { - Parent = parent, - Properties = properties, - Children = children, - Icon = icon, - Cover = cover + Parent = _parent, + Properties = _properties, + Children = _children, + Icon = _icon, + Cover = _cover }; } } diff --git a/Src/Notion.Client/Api/Pages/RequestParams/PagesUpdateParameters.cs b/Src/Notion.Client/Api/Pages/RequestParams/PagesUpdateParameters.cs index 40f06b12..6845f27b 100644 --- a/Src/Notion.Client/Api/Pages/RequestParams/PagesUpdateParameters.cs +++ b/Src/Notion.Client/Api/Pages/RequestParams/PagesUpdateParameters.cs @@ -5,16 +5,16 @@ namespace Notion.Client { public class PagesUpdateParameters : IPagesUpdateBodyParameters { - [JsonProperty("archived")] - public bool Archived { get; set; } - - [JsonProperty("properties")] - public IDictionary Properties { get; set; } - [JsonProperty("icon")] public IPageIcon Icon { get; set; } [JsonProperty("cover")] public FileObject Cover { get; set; } + + [JsonProperty("in_trash")] + public bool InTrash { get; set; } + + [JsonProperty("properties")] + public IDictionary Properties { get; set; } } } diff --git a/Src/Notion.Client/Api/Search/ISearchClient.cs b/Src/Notion.Client/Api/Search/ISearchClient.cs index 883c86ba..accee1e1 100644 --- a/Src/Notion.Client/Api/Search/ISearchClient.cs +++ b/Src/Notion.Client/Api/Search/ISearchClient.cs @@ -1,9 +1,24 @@ -using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMemberInSuper.Global")] public interface ISearchClient { - Task> SearchAsync(SearchParameters parameters); + /// + /// Searches all original pages, databases, and child pages/databases that are shared with the integration. + /// It will not return linked databases, since these duplicate their source databases. + /// + /// Search filters and body parameters + /// + /// + /// + /// + Task SearchAsync( + SearchRequest request, + CancellationToken cancellationToken = default + ); } } diff --git a/Src/Notion.Client/Api/Search/Parameters/ISearchBodyParameters.cs b/Src/Notion.Client/Api/Search/Request/ISearchBodyParameters.cs similarity index 100% rename from Src/Notion.Client/Api/Search/Parameters/ISearchBodyParameters.cs rename to Src/Notion.Client/Api/Search/Request/ISearchBodyParameters.cs diff --git a/Src/Notion.Client/Api/Search/Parameters/SearchDirection.cs b/Src/Notion.Client/Api/Search/Request/SearchDirection.cs similarity index 59% rename from Src/Notion.Client/Api/Search/Parameters/SearchDirection.cs rename to Src/Notion.Client/Api/Search/Request/SearchDirection.cs index fdac0b59..da52b11b 100644 --- a/Src/Notion.Client/Api/Search/Parameters/SearchDirection.cs +++ b/Src/Notion.Client/Api/Search/Request/SearchDirection.cs @@ -1,7 +1,9 @@ -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMember.Global")] public enum SearchDirection { [EnumMember(Value = "ascending")] diff --git a/Src/Notion.Client/Api/Search/Parameters/SearchFilter.cs b/Src/Notion.Client/Api/Search/Request/SearchFilter.cs similarity index 56% rename from Src/Notion.Client/Api/Search/Parameters/SearchFilter.cs rename to Src/Notion.Client/Api/Search/Request/SearchFilter.cs index cc84a9a3..b36ca873 100644 --- a/Src/Notion.Client/Api/Search/Parameters/SearchFilter.cs +++ b/Src/Notion.Client/Api/Search/Request/SearchFilter.cs @@ -1,12 +1,16 @@ -using Newtonsoft.Json; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace Notion.Client { + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] + [SuppressMessage("ReSharper", "UnusedMember.Global")] public class SearchFilter { [JsonConverter(typeof(StringEnumConverter))] public SearchObjectType Value { get; set; } + public string Property => "object"; } } diff --git a/Src/Notion.Client/Api/Search/Parameters/SearchObjectType.cs b/Src/Notion.Client/Api/Search/Request/SearchObjectType.cs similarity index 57% rename from Src/Notion.Client/Api/Search/Parameters/SearchObjectType.cs rename to Src/Notion.Client/Api/Search/Request/SearchObjectType.cs index 78a012df..e3e6181b 100644 --- a/Src/Notion.Client/Api/Search/Parameters/SearchObjectType.cs +++ b/Src/Notion.Client/Api/Search/Request/SearchObjectType.cs @@ -1,7 +1,9 @@ -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMember.Global")] public enum SearchObjectType { [EnumMember(Value = "page")] diff --git a/Src/Notion.Client/Api/Search/Parameters/SearchParameters.cs b/Src/Notion.Client/Api/Search/Request/SearchRequest.cs similarity index 82% rename from Src/Notion.Client/Api/Search/Parameters/SearchParameters.cs rename to Src/Notion.Client/Api/Search/Request/SearchRequest.cs index ae02c0f4..a9ac887f 100644 --- a/Src/Notion.Client/Api/Search/Parameters/SearchParameters.cs +++ b/Src/Notion.Client/Api/Search/Request/SearchRequest.cs @@ -1,11 +1,15 @@ namespace Notion.Client { - public class SearchParameters : ISearchBodyParameters + public class SearchRequest : ISearchBodyParameters { public string Query { get; set; } + public SearchSort Sort { get; set; } + public SearchFilter Filter { get; set; } + public string StartCursor { get; set; } + public int? PageSize { get; set; } } } diff --git a/Src/Notion.Client/Api/Search/Parameters/SearchSort.cs b/Src/Notion.Client/Api/Search/Request/SearchSort.cs similarity index 100% rename from Src/Notion.Client/Api/Search/Parameters/SearchSort.cs rename to Src/Notion.Client/Api/Search/Request/SearchSort.cs diff --git a/Src/Notion.Client/Api/Search/Response/SearchResponse.cs b/Src/Notion.Client/Api/Search/Response/SearchResponse.cs new file mode 100644 index 00000000..2be0e2f1 --- /dev/null +++ b/Src/Notion.Client/Api/Search/Response/SearchResponse.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class SearchResponse : PaginatedList + { + [JsonProperty("page_or_database")] + public Dictionary PageOrDatabase { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Search/SearchClient.cs b/Src/Notion.Client/Api/Search/SearchClient.cs index 87de9270..492e0940 100644 --- a/Src/Notion.Client/Api/Search/SearchClient.cs +++ b/Src/Notion.Client/Api/Search/SearchClient.cs @@ -1,24 +1,27 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; using static Notion.Client.ApiEndpoints; namespace Notion.Client { - public class SearchClient : ISearchClient + public sealed class SearchClient : ISearchClient { - private readonly IRestClient client; + private readonly IRestClient _client; public SearchClient(IRestClient client) { - this.client = client; + _client = client; } - public async Task> SearchAsync(SearchParameters parameters) + public async Task SearchAsync( + SearchRequest request, + CancellationToken cancellationToken = default) { var url = SearchApiUrls.Search(); - var body = (ISearchBodyParameters)parameters; + var body = (ISearchBodyParameters)request; - return await client.PostAsync>(url, body); + return await _client.PostAsync(url, body, cancellationToken: cancellationToken); } } } diff --git a/Src/Notion.Client/Api/Users/IUsersClient.cs b/Src/Notion.Client/Api/Users/IUsersClient.cs index 95427f2b..bc0c3d8a 100644 --- a/Src/Notion.Client/Api/Users/IUsersClient.cs +++ b/Src/Notion.Client/Api/Users/IUsersClient.cs @@ -1,16 +1,51 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; +using Notion.Client.List.Request; namespace Notion.Client { public interface IUsersClient { - Task RetrieveAsync(string userId); - Task> ListAsync(); + /// + /// Retrieves a User using the ID specified. + /// + /// Identifier for a Notion user + /// + /// + /// + Task RetrieveAsync(string userId, CancellationToken cancellationToken = default); + + /// + /// Returns a paginated list of Users for the workspace. + /// The response may contain fewer than page_size of results. + /// + /// + /// + /// + /// + Task ListAsync(CancellationToken cancellationToken = default); + + /// + /// Returns a paginated list of Users for the workspace. + /// The response may contain fewer than page_size of results. + /// + /// + /// + /// + /// + /// + Task ListAsync( + ListUsersRequest listUsersRequest, + CancellationToken cancellationToken = default + ); /// - /// Retrieves the bot User associated with the API token provided in the authorization header. + /// Retrieves the bot User associated with the API token provided in the authorization header. /// - /// User object of type bot having an owner field with information about the person who authorized the integration. - Task MeAsync(); + /// + /// object of type bot having an owner field with information about the person who authorized + /// the integration. + /// + Task MeAsync(CancellationToken cancellationToken = default); } } diff --git a/Src/Notion.Client/Api/Users/List/Request/ListUsersRequest.cs b/Src/Notion.Client/Api/Users/List/Request/ListUsersRequest.cs new file mode 100644 index 00000000..f3684d09 --- /dev/null +++ b/Src/Notion.Client/Api/Users/List/Request/ListUsersRequest.cs @@ -0,0 +1,13 @@ +namespace Notion.Client.List.Request +{ + public interface IListUsersQueryParameters : IPaginationParameters + { + } + + public class ListUsersRequest : IListUsersQueryParameters + { + public string StartCursor { get; set; } + + public int? PageSize { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Users/List/Response/ListUsersResponse.cs b/Src/Notion.Client/Api/Users/List/Response/ListUsersResponse.cs new file mode 100644 index 00000000..2d30237f --- /dev/null +++ b/Src/Notion.Client/Api/Users/List/Response/ListUsersResponse.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ListUsersResponse : PaginatedList + { + [JsonProperty("user")] + public Dictionary User { get; set; } + } +} diff --git a/Src/Notion.Client/Api/Users/List/UsersClient.cs b/Src/Notion.Client/Api/Users/List/UsersClient.cs new file mode 100644 index 00000000..ce13633a --- /dev/null +++ b/Src/Notion.Client/Api/Users/List/UsersClient.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Notion.Client.List.Request; + +namespace Notion.Client +{ + public partial class UsersClient + { + public async Task ListAsync(CancellationToken cancellationToken = default) + { + return await _client.GetAsync( + ApiEndpoints.UsersApiUrls.List(), + cancellationToken: cancellationToken + ); + } + + public async Task ListAsync( + ListUsersRequest listUsersRequest, + CancellationToken cancellationToken = default + ) + { + var queryParameters = (IListUsersQueryParameters)listUsersRequest; + + var queryParams = new Dictionary + { + { "start_cursor", queryParameters?.StartCursor }, + { "page_size", queryParameters?.PageSize?.ToString() } + }; + + return await _client.GetAsync( + ApiEndpoints.UsersApiUrls.List(), + queryParams, + cancellationToken: cancellationToken + ); + } + } +} diff --git a/Src/Notion.Client/Api/Users/UsersClient.cs b/Src/Notion.Client/Api/Users/UsersClient.cs index 5c745f72..24681b53 100644 --- a/Src/Notion.Client/Api/Users/UsersClient.cs +++ b/Src/Notion.Client/Api/Users/UsersClient.cs @@ -1,9 +1,10 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; using static Notion.Client.ApiEndpoints; namespace Notion.Client { - public class UsersClient : IUsersClient + public partial class UsersClient : IUsersClient { private readonly IRestClient _client; @@ -12,23 +13,21 @@ public UsersClient(IRestClient client) _client = client; } - public async Task RetrieveAsync(string userId) + public async Task RetrieveAsync(string userId, CancellationToken cancellationToken = default) { - return await _client.GetAsync(UsersApiUrls.Retrieve(userId)); - } - - public async Task> ListAsync() - { - return await _client.GetAsync>(UsersApiUrls.List()); + return await _client.GetAsync(UsersApiUrls.Retrieve(userId), cancellationToken: cancellationToken); } /// - /// Retrieves the bot User associated with the API token provided in the authorization header. + /// Retrieves the bot User associated with the API token provided in the authorization header. /// - /// User object of type bot having an owner field with information about the person who authorized the integration. - public async Task MeAsync() + /// + /// User object of type bot having an owner field with information about the person who authorized the + /// integration. + /// + public async Task MeAsync(CancellationToken cancellationToken = default) { - return await _client.GetAsync(UsersApiUrls.Me()); + return await _client.GetAsync(UsersApiUrls.Me(), cancellationToken: cancellationToken); } } } diff --git a/Src/Notion.Client/Constants.cs b/Src/Notion.Client/Constants.cs index 5844bd6e..779bcb47 100644 --- a/Src/Notion.Client/Constants.cs +++ b/Src/Notion.Client/Constants.cs @@ -1,11 +1,12 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Notion.UnitTests")] + namespace Notion.Client { - internal class Constants + internal static class Constants { - internal static string BASE_URL = "https://api.notion.com/"; - internal static string DEFAULT_NOTION_VERSION = "2021-08-16"; + internal const string BaseUrl = "https://api.notion.com/"; + internal const string DefaultNotionVersion = "2022-06-28"; } } diff --git a/Src/Notion.Client/DI/ServiceCollectionExtensions.cs b/Src/Notion.Client/DI/ServiceCollectionExtensions.cs index a4aed7fd..9a658077 100644 --- a/Src/Notion.Client/DI/ServiceCollectionExtensions.cs +++ b/Src/Notion.Client/DI/ServiceCollectionExtensions.cs @@ -1,13 +1,18 @@ using System; +using System.Diagnostics.CodeAnalysis; using Notion.Client; namespace Microsoft.Extensions.DependencyInjection { + [SuppressMessage("ReSharper", "UnusedType.Global")] + [SuppressMessage("ReSharper", "UnusedMember.Global")] public static class ServiceCollectionExtensions { - public static IServiceCollection AddNotionClient(this IServiceCollection services, Action options) + public static IServiceCollection AddNotionClient( + this IServiceCollection services, + Action options) { - services.AddSingleton(sp => + services.AddSingleton(_ => { var clientOptions = new ClientOptions(); options?.Invoke(clientOptions); diff --git a/Src/Notion.Client/Extensions/EnumExtensions.cs b/Src/Notion.Client/Extensions/EnumExtensions.cs index 987104b8..cd60aebd 100644 --- a/Src/Notion.Client/Extensions/EnumExtensions.cs +++ b/Src/Notion.Client/Extensions/EnumExtensions.cs @@ -1,15 +1,18 @@ -using System.Linq; +using System; +using System.Linq; using System.Runtime.Serialization; namespace Notion.Client.Extensions { public static class EnumExtensions { - public static string GetEnumMemberValue(this T enumValue) where T : System.Enum + public static string GetEnumMemberValue(this T enumValue) where T : Enum { var enumType = typeof(T); var memInfo = enumType.GetMember(enumValue.ToString()); - var attr = memInfo.FirstOrDefault()?.GetCustomAttributes(false).OfType().FirstOrDefault(); + + var attr = memInfo.FirstOrDefault()?.GetCustomAttributes(false).OfType() + .FirstOrDefault(); if (attr != null) { diff --git a/Src/Notion.Client/Extensions/HttpResponseMessageExtensions.cs b/Src/Notion.Client/Extensions/HttpResponseMessageExtensions.cs index 9b01580d..cc6f9698 100644 --- a/Src/Notion.Client/Extensions/HttpResponseMessageExtensions.cs +++ b/Src/Notion.Client/Extensions/HttpResponseMessageExtensions.cs @@ -7,29 +7,19 @@ namespace Notion.Client.Extensions { internal static class HttpResponseMessageExtensions { - internal static async Task ParseStreamAsync(this HttpResponseMessage response, JsonSerializerSettings serializerSettings = null) + internal static async Task ParseStreamAsync( + this HttpResponseMessage response, + JsonSerializerSettings serializerSettings = null) { - using (Stream stream = await response.Content.ReadAsStreamAsync()) - { - using (StreamReader streamReader = new StreamReader(stream)) - { - using (JsonReader jsonReader = new JsonTextReader(streamReader)) - { - JsonSerializer serializer = null; + using var stream = await response.Content.ReadAsStreamAsync(); + using var streamReader = new StreamReader(stream); + using JsonReader jsonReader = new JsonTextReader(streamReader); - if (serializerSettings == null) - { - serializer = JsonSerializer.CreateDefault(); - } - else - { - serializer = JsonSerializer.Create(serializerSettings); - } + var serializer = serializerSettings == null + ? JsonSerializer.CreateDefault() + : JsonSerializer.Create(serializerSettings); - return serializer.Deserialize(jsonReader); - } - } - } + return serializer.Deserialize(jsonReader); } } } diff --git a/Src/Notion.Client/Logging/Log.cs b/Src/Notion.Client/Logging/Log.cs index 7fda66a5..47beea5e 100644 --- a/Src/Notion.Client/Logging/Log.cs +++ b/Src/Notion.Client/Logging/Log.cs @@ -1,25 +1,27 @@ using System; +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; namespace Notion.Client { internal static class Log { - internal static ILogger logger; + internal static ILogger Logger; internal static void Trace(string message, params object[] args) { - logger?.LogTrace(message, args); + Logger?.LogTrace(message, args); } + [SuppressMessage("ReSharper", "UnusedMember.Global")] internal static void Information(string message, params object[] args) { - logger?.LogInformation(message, args); + Logger?.LogInformation(message, args); } internal static void Error(Exception ex, string message, params object[] args) { - logger?.LogError(ex, message, args); + Logger?.LogError(ex, message, args); } } } diff --git a/Src/Notion.Client/Logging/NotionClientLogging.cs b/Src/Notion.Client/Logging/NotionClientLogging.cs index b77ff15e..07a18514 100644 --- a/Src/Notion.Client/Logging/NotionClientLogging.cs +++ b/Src/Notion.Client/Logging/NotionClientLogging.cs @@ -1,16 +1,19 @@ -using Microsoft.Extensions.Logging; +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Logging; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedType.Global")] + [SuppressMessage("ReSharper", "UnusedMember.Global")] public static class NotionClientLogging { - internal static ILoggerFactory factory; + private static ILoggerFactory _factory; public static void ConfigureLogger(ILoggerFactory loggerFactory) { - factory = loggerFactory; + _factory = loggerFactory; - Log.logger = Log.logger == null ? factory?.CreateLogger("Notion.Client") : Log.logger; + Log.Logger = Log.Logger == null ? _factory?.CreateLogger("Notion.Client") : Log.Logger; } } } diff --git a/Src/Notion.Client/Models/Blocks/AudioBlock.cs b/Src/Notion.Client/Models/Blocks/AudioBlock.cs index 040f346b..1ba1a4a3 100644 --- a/Src/Notion.Client/Models/Blocks/AudioBlock.cs +++ b/Src/Notion.Client/Models/Blocks/AudioBlock.cs @@ -4,9 +4,9 @@ namespace Notion.Client { public class AudioBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Audio; - [JsonProperty("audio")] public FileObject Audio { get; set; } + + public override BlockType Type => BlockType.Audio; } } diff --git a/Src/Notion.Client/Models/Blocks/Block.cs b/Src/Notion.Client/Models/Blocks/Block.cs index 9ba90612..fa1ef612 100644 --- a/Src/Notion.Client/Models/Blocks/Block.cs +++ b/Src/Notion.Client/Models/Blocks/Block.cs @@ -1,4 +1,6 @@ -namespace Notion.Client +using System; + +namespace Notion.Client { public abstract class Block : IBlock { @@ -8,10 +10,21 @@ public abstract class Block : IBlock public virtual BlockType Type { get; set; } - public string CreatedTime { get; set; } + public DateTime CreatedTime { get; set; } - public string LastEditedTime { get; set; } + public DateTime LastEditedTime { get; set; } public virtual bool HasChildren { get; set; } + + public bool InTrash { get; set; } + + public PartialUser CreatedBy { get; set; } + + public PartialUser LastEditedBy { get; set; } + + /// + /// Information about the block's parent. + /// + public IBlockParent Parent { get; set; } } } diff --git a/Src/Notion.Client/Models/Blocks/BlockType.cs b/Src/Notion.Client/Models/Blocks/BlockType.cs index b46e6c9a..92de4b6b 100644 --- a/Src/Notion.Client/Models/Blocks/BlockType.cs +++ b/Src/Notion.Client/Models/Blocks/BlockType.cs @@ -1,4 +1,5 @@ -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; namespace Notion.Client { @@ -8,12 +9,15 @@ public enum BlockType Paragraph, [EnumMember(Value = "heading_1")] + [SuppressMessage("ReSharper", "InconsistentNaming")] Heading_1, [EnumMember(Value = "heading_2")] + [SuppressMessage("ReSharper", "InconsistentNaming")] Heading_2, [EnumMember(Value = "heading_3")] + [SuppressMessage("ReSharper", "InconsistentNaming")] Heading_3, [EnumMember(Value = "bulleted_list_item")] @@ -82,6 +86,24 @@ public enum BlockType [EnumMember(Value = "column_list")] ColumnList, + [EnumMember(Value = "template")] + Template, + + [EnumMember(Value = "link_to_page")] + LinkToPage, + + [EnumMember(Value = "synced_block")] + SyncedBlock, + + [EnumMember(Value = "table")] + Table, + + [EnumMember(Value = "table_row")] + TableRow, + + [EnumMember(Value = "link_preview")] + LinkPreview, + [EnumMember(Value = "unsupported")] Unsupported } diff --git a/Src/Notion.Client/Models/Blocks/BookmarkBlock.cs b/Src/Notion.Client/Models/Blocks/BookmarkBlock.cs index 735b1725..73099c82 100644 --- a/Src/Notion.Client/Models/Blocks/BookmarkBlock.cs +++ b/Src/Notion.Client/Models/Blocks/BookmarkBlock.cs @@ -5,11 +5,11 @@ namespace Notion.Client { public class BookmarkBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Bookmark; - [JsonProperty("bookmark")] public Info Bookmark { get; set; } + public override BlockType Type => BlockType.Bookmark; + public class Info { [JsonProperty("url")] diff --git a/Src/Notion.Client/Models/Blocks/BreadcrumbBlock.cs b/Src/Notion.Client/Models/Blocks/BreadcrumbBlock.cs index 73ae614d..d5585a40 100644 --- a/Src/Notion.Client/Models/Blocks/BreadcrumbBlock.cs +++ b/Src/Notion.Client/Models/Blocks/BreadcrumbBlock.cs @@ -4,11 +4,11 @@ namespace Notion.Client { public class BreadcrumbBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Breadcrumb; - [JsonProperty("breadcrumb")] public Data Breadcrumb { get; set; } + public override BlockType Type => BlockType.Breadcrumb; + public class Data { } diff --git a/Src/Notion.Client/Models/Blocks/BulletedListItemBlock.cs b/Src/Notion.Client/Models/Blocks/BulletedListItemBlock.cs index 15d168af..c823b4bd 100644 --- a/Src/Notion.Client/Models/Blocks/BulletedListItemBlock.cs +++ b/Src/Notion.Client/Models/Blocks/BulletedListItemBlock.cs @@ -1,19 +1,24 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { public class BulletedListItemBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.BulletedListItem; - [JsonProperty("bulleted_list_item")] public Info BulletedListItem { get; set; } + public override BlockType Type => BlockType.BulletedListItem; + public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } [JsonProperty("children")] public IEnumerable Children { get; set; } diff --git a/Src/Notion.Client/Models/Blocks/CalloutBlock.cs b/Src/Notion.Client/Models/Blocks/CalloutBlock.cs index 9fed148a..d4ef61c4 100644 --- a/Src/Notion.Client/Models/Blocks/CalloutBlock.cs +++ b/Src/Notion.Client/Models/Blocks/CalloutBlock.cs @@ -1,23 +1,28 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { public class CalloutBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Callout; - [JsonProperty("callout")] public Info Callout { get; set; } + public override BlockType Type => BlockType.Callout; + public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } [JsonProperty("icon")] public IPageIcon Icon { get; set; } + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + [JsonProperty("children")] public IEnumerable Children { get; set; } } diff --git a/Src/Notion.Client/Models/Blocks/ChildDatabaseBlock.cs b/Src/Notion.Client/Models/Blocks/ChildDatabaseBlock.cs index 273836a9..e56fcadb 100644 --- a/Src/Notion.Client/Models/Blocks/ChildDatabaseBlock.cs +++ b/Src/Notion.Client/Models/Blocks/ChildDatabaseBlock.cs @@ -4,11 +4,11 @@ namespace Notion.Client { public class ChildDatabaseBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.ChildDatabase; - [JsonProperty("child_database")] public Info ChildDatabase { get; set; } + public override BlockType Type => BlockType.ChildDatabase; + public class Info { [JsonProperty("title")] diff --git a/Src/Notion.Client/Models/Blocks/ChildPageBlock.cs b/Src/Notion.Client/Models/Blocks/ChildPageBlock.cs index 91c8f343..216b0d82 100644 --- a/Src/Notion.Client/Models/Blocks/ChildPageBlock.cs +++ b/Src/Notion.Client/Models/Blocks/ChildPageBlock.cs @@ -4,11 +4,11 @@ namespace Notion.Client { public class ChildPageBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.ChildPage; - [JsonProperty("child_page")] public Info ChildPage { get; set; } + public override BlockType Type => BlockType.ChildPage; + public class Info { [JsonProperty("title")] diff --git a/Src/Notion.Client/Models/Blocks/CodeBlock.cs b/Src/Notion.Client/Models/Blocks/CodeBlock.cs index 7eb4205d..9cb2c350 100644 --- a/Src/Notion.Client/Models/Blocks/CodeBlock.cs +++ b/Src/Notion.Client/Models/Blocks/CodeBlock.cs @@ -5,18 +5,21 @@ namespace Notion.Client { public class CodeBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Code; - [JsonProperty("code")] public Info Code { get; set; } + public override BlockType Type => BlockType.Code; + public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } [JsonProperty("language")] public string Language { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } } } } diff --git a/Src/Notion.Client/Models/Blocks/Color.cs b/Src/Notion.Client/Models/Blocks/Color.cs new file mode 100644 index 00000000..46e38e2a --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Color.cs @@ -0,0 +1,66 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; + +namespace Notion.Client +{ + [SuppressMessage("ReSharper", "UnusedMember.Global")] + public enum Color + { + [EnumMember(Value = "default")] + Default, + + [EnumMember(Value = "gray")] + Gray, + + [EnumMember(Value = "brown")] + Brown, + + [EnumMember(Value = "orange")] + Orange, + + [EnumMember(Value = "yellow")] + Yellow, + + [EnumMember(Value = "green")] + Green, + + [EnumMember(Value = "blue")] + Blue, + + [EnumMember(Value = "purple")] + Purple, + + [EnumMember(Value = "pink")] + Pink, + + [EnumMember(Value = "red")] + Red, + + [EnumMember(Value = "gray_background")] + GrayBackground, + + [EnumMember(Value = "brown_background")] + BrownBackground, + + [EnumMember(Value = "orange_background")] + OrangeBackground, + + [EnumMember(Value = "yellow_background")] + YellowBackground, + + [EnumMember(Value = "green_background")] + GreenBackground, + + [EnumMember(Value = "blue_background")] + BlueBackground, + + [EnumMember(Value = "purple_background")] + PurpleBackground, + + [EnumMember(Value = "pink_background")] + PinkBackground, + + [EnumMember(Value = "red_background")] + RedBackground + } +} diff --git a/Src/Notion.Client/Models/Blocks/ColumnListBlock.cs b/Src/Notion.Client/Models/Blocks/ColumnListBlock.cs index f86ecba0..59bd6245 100644 --- a/Src/Notion.Client/Models/Blocks/ColumnListBlock.cs +++ b/Src/Notion.Client/Models/Blocks/ColumnListBlock.cs @@ -5,11 +5,11 @@ namespace Notion.Client { public class ColumnListBlock : Block, INonColumnBlock { - public override BlockType Type => BlockType.ColumnList; - [JsonProperty("column_list")] public Info ColumnList { get; set; } + public override BlockType Type => BlockType.ColumnList; + public class Info { [JsonProperty("children")] diff --git a/Src/Notion.Client/Models/Blocks/DividerBlock.cs b/Src/Notion.Client/Models/Blocks/DividerBlock.cs index 61e0301b..26c55783 100644 --- a/Src/Notion.Client/Models/Blocks/DividerBlock.cs +++ b/Src/Notion.Client/Models/Blocks/DividerBlock.cs @@ -4,11 +4,11 @@ namespace Notion.Client { public class DividerBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Divider; - [JsonProperty("divider")] public Data Divider { get; set; } + public override BlockType Type => BlockType.Divider; + public class Data { } diff --git a/Src/Notion.Client/Models/Blocks/EmbedBlock.cs b/Src/Notion.Client/Models/Blocks/EmbedBlock.cs index 8332b683..92c1dfdd 100644 --- a/Src/Notion.Client/Models/Blocks/EmbedBlock.cs +++ b/Src/Notion.Client/Models/Blocks/EmbedBlock.cs @@ -1,18 +1,22 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; namespace Notion.Client { public class EmbedBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Embed; - [JsonProperty("embed")] public Info Embed { get; set; } + public override BlockType Type => BlockType.Embed; + public class Info { [JsonProperty("url")] public string Url { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } } } } diff --git a/Src/Notion.Client/Models/Blocks/EquationBlock.cs b/Src/Notion.Client/Models/Blocks/EquationBlock.cs index 310a76df..411c4e3b 100644 --- a/Src/Notion.Client/Models/Blocks/EquationBlock.cs +++ b/Src/Notion.Client/Models/Blocks/EquationBlock.cs @@ -4,11 +4,11 @@ namespace Notion.Client { public class EquationBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Equation; - [JsonProperty("equation")] public Info Equation { get; set; } + public override BlockType Type => BlockType.Equation; + public class Info { [JsonProperty("expression")] diff --git a/Src/Notion.Client/Models/Blocks/FileBlock.cs b/Src/Notion.Client/Models/Blocks/FileBlock.cs index 5495e4bc..7749a693 100644 --- a/Src/Notion.Client/Models/Blocks/FileBlock.cs +++ b/Src/Notion.Client/Models/Blocks/FileBlock.cs @@ -4,9 +4,9 @@ namespace Notion.Client { public class FileBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.File; - [JsonProperty("file")] public FileObject File { get; set; } + + public override BlockType Type => BlockType.File; } } diff --git a/Src/Notion.Client/Models/Blocks/HeadingOneBlock.cs b/Src/Notion.Client/Models/Blocks/HeadingOneBlock.cs index d9c107c3..1e8474a4 100644 --- a/Src/Notion.Client/Models/Blocks/HeadingOneBlock.cs +++ b/Src/Notion.Client/Models/Blocks/HeadingOneBlock.cs @@ -1,21 +1,29 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { public class HeadingOneBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Heading_1; - [JsonProperty("heading_1")] + [SuppressMessage("ReSharper", "InconsistentNaming")] public Info Heading_1 { get; set; } - public override bool HasChildren => false; + public override BlockType Type => BlockType.Heading_1; public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("is_toggleable")] + public bool IsToggleable { get; set; } } } } diff --git a/Src/Notion.Client/Models/Blocks/HeadingThreeBlock.cs b/Src/Notion.Client/Models/Blocks/HeadingThreeBlock.cs new file mode 100644 index 00000000..e300a511 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/HeadingThreeBlock.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class HeadingThreeBlock : Block, IColumnChildrenBlock, INonColumnBlock + { + [JsonProperty("heading_3")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public Info Heading_3 { get; set; } + + public override BlockType Type => BlockType.Heading_3; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("is_toggleable")] + public bool IsToggleable { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/HeadingThreeeBlock.cs b/Src/Notion.Client/Models/Blocks/HeadingThreeeBlock.cs deleted file mode 100644 index 0728f200..00000000 --- a/Src/Notion.Client/Models/Blocks/HeadingThreeeBlock.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace Notion.Client -{ - public class HeadingThreeeBlock : Block, IColumnChildrenBlock, INonColumnBlock - { - public override BlockType Type => BlockType.Heading_3; - - [JsonProperty("heading_3")] - public Info Heading_3 { get; set; } - - public override bool HasChildren => false; - - public class Info - { - [JsonProperty("text")] - public IEnumerable Text { get; set; } - } - } -} diff --git a/Src/Notion.Client/Models/Blocks/HeadingTwoBlock.cs b/Src/Notion.Client/Models/Blocks/HeadingTwoBlock.cs index c9caec09..3be849a8 100644 --- a/Src/Notion.Client/Models/Blocks/HeadingTwoBlock.cs +++ b/Src/Notion.Client/Models/Blocks/HeadingTwoBlock.cs @@ -1,21 +1,29 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { public class HeadingTwoBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Heading_2; - [JsonProperty("heading_2")] + [SuppressMessage("ReSharper", "InconsistentNaming")] public Info Heading_2 { get; set; } - public override bool HasChildren => false; + public override BlockType Type => BlockType.Heading_2; public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("is_toggleable")] + public bool IsToggleable { get; set; } } } } diff --git a/Src/Notion.Client/Models/Blocks/IBlock.cs b/Src/Notion.Client/Models/Blocks/IBlock.cs index e36f371a..b06a3646 100644 --- a/Src/Notion.Client/Models/Blocks/IBlock.cs +++ b/Src/Notion.Client/Models/Blocks/IBlock.cs @@ -5,46 +5,52 @@ namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(AudioBlock), BlockType.Audio)] - [JsonSubtypes.KnownSubType(typeof(BookmarkBlock), BlockType.Bookmark)] - [JsonSubtypes.KnownSubType(typeof(BreadcrumbBlock), BlockType.Breadcrumb)] - [JsonSubtypes.KnownSubType(typeof(BulletedListItemBlock), BlockType.BulletedListItem)] - [JsonSubtypes.KnownSubType(typeof(CalloutBlock), BlockType.Callout)] - [JsonSubtypes.KnownSubType(typeof(ChildPageBlock), BlockType.ChildPage)] - [JsonSubtypes.KnownSubType(typeof(ChildDatabaseBlock), BlockType.ChildDatabase)] - [JsonSubtypes.KnownSubType(typeof(CodeBlock), BlockType.Code)] - [JsonSubtypes.KnownSubType(typeof(ColumnBlock), BlockType.Column)] - [JsonSubtypes.KnownSubType(typeof(ColumnListBlock), BlockType.ColumnList)] - [JsonSubtypes.KnownSubType(typeof(DividerBlock), BlockType.Divider)] - [JsonSubtypes.KnownSubType(typeof(EmbedBlock), BlockType.Embed)] - [JsonSubtypes.KnownSubType(typeof(EquationBlock), BlockType.Equation)] - [JsonSubtypes.KnownSubType(typeof(FileBlock), BlockType.File)] - [JsonSubtypes.KnownSubType(typeof(HeadingOneBlock), BlockType.Heading_1)] - [JsonSubtypes.KnownSubType(typeof(HeadingTwoBlock), BlockType.Heading_2)] - [JsonSubtypes.KnownSubType(typeof(HeadingThreeeBlock), BlockType.Heading_3)] - [JsonSubtypes.KnownSubType(typeof(ImageBlock), BlockType.Image)] - [JsonSubtypes.KnownSubType(typeof(NumberedListItemBlock), BlockType.NumberedListItem)] - [JsonSubtypes.KnownSubType(typeof(ParagraphBlock), BlockType.Paragraph)] - [JsonSubtypes.KnownSubType(typeof(PDFBlock), BlockType.PDF)] - [JsonSubtypes.KnownSubType(typeof(QuoteBlock), BlockType.Quote)] - [JsonSubtypes.KnownSubType(typeof(TableOfContentsBlock), BlockType.TableOfContents)] - [JsonSubtypes.KnownSubType(typeof(ToDoBlock), BlockType.ToDo)] - [JsonSubtypes.KnownSubType(typeof(ToggleBlock), BlockType.Toggle)] - [JsonSubtypes.KnownSubType(typeof(VideoBlock), BlockType.Video)] - [JsonSubtypes.KnownSubType(typeof(UnsupportedBlock), BlockType.Unsupported)] - public interface IBlock : IObject + [JsonSubtypes.KnownSubTypeAttribute(typeof(AudioBlock), BlockType.Audio)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(BookmarkBlock), BlockType.Bookmark)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(BreadcrumbBlock), BlockType.Breadcrumb)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(BulletedListItemBlock), BlockType.BulletedListItem)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CalloutBlock), BlockType.Callout)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ChildPageBlock), BlockType.ChildPage)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ChildDatabaseBlock), BlockType.ChildDatabase)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CodeBlock), BlockType.Code)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ColumnBlock), BlockType.Column)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ColumnListBlock), BlockType.ColumnList)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(DividerBlock), BlockType.Divider)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(EmbedBlock), BlockType.Embed)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(EquationBlock), BlockType.Equation)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(FileBlock), BlockType.File)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(HeadingOneBlock), BlockType.Heading_1)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(HeadingTwoBlock), BlockType.Heading_2)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(HeadingThreeBlock), BlockType.Heading_3)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ImageBlock), BlockType.Image)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(LinkPreviewBlock), BlockType.LinkPreview)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(LinkToPageBlock), BlockType.LinkToPage)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(NumberedListItemBlock), BlockType.NumberedListItem)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ParagraphBlock), BlockType.Paragraph)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(PDFBlock), BlockType.PDF)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(QuoteBlock), BlockType.Quote)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(SyncedBlockBlock), BlockType.SyncedBlock)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(TableBlock), BlockType.Table)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(TableRowBlock), BlockType.TableRow)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(TableOfContentsBlock), BlockType.TableOfContents)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(TemplateBlock), BlockType.Template)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ToDoBlock), BlockType.ToDo)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ToggleBlock), BlockType.Toggle)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(VideoBlock), BlockType.Video)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(UnsupportedBlock), BlockType.Unsupported)] + public interface IBlock : IObject, IObjectModificationData { [JsonProperty("type")] [JsonConverter(typeof(StringEnumConverter))] BlockType Type { get; set; } - [JsonProperty("created_time")] - string CreatedTime { get; set; } - - [JsonProperty("last_edited_time")] - string LastEditedTime { get; set; } - [JsonProperty("has_children")] bool HasChildren { get; set; } + + [JsonProperty("in_trash")] + bool InTrash { get; set; } + + [JsonProperty("parent")] + IBlockParent Parent { get; set; } } } diff --git a/Src/Notion.Client/Models/Blocks/IBlockParent.cs b/Src/Notion.Client/Models/Blocks/IBlockParent.cs new file mode 100644 index 00000000..b2f8783f --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/IBlockParent.cs @@ -0,0 +1,14 @@ +using JsonSubTypes; +using Newtonsoft.Json; + +namespace Notion.Client +{ + [JsonConverter(typeof(JsonSubtypes), "type")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(DatabaseParent), ParentType.DatabaseId)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(PageParent), ParentType.PageId)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(WorkspaceParent), ParentType.Workspace)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(BlockParent), ParentType.BlockId)] + public interface IBlockParent : IParent + { + } +} diff --git a/Src/Notion.Client/Models/Blocks/IColumnChildrenBlock.cs b/Src/Notion.Client/Models/Blocks/IColumnChildrenBlock.cs index 9eaa5622..769487c8 100644 --- a/Src/Notion.Client/Models/Blocks/IColumnChildrenBlock.cs +++ b/Src/Notion.Client/Models/Blocks/IColumnChildrenBlock.cs @@ -1,65 +1,17 @@ -using JsonSubTypes; -using Newtonsoft.Json; - -namespace Notion.Client +namespace Notion.Client { - //[JsonConverter(typeof(JsonSubtypes), "type")] - //[JsonSubtypes.KnownSubType(typeof(AudioBlock), BlockType.Audio)] - //[JsonSubtypes.KnownSubType(typeof(BookmarkBlock), BlockType.Bookmark)] - //[JsonSubtypes.KnownSubType(typeof(BreadcrumbBlock), BlockType.Breadcrumb)] - //[JsonSubtypes.KnownSubType(typeof(BulletedListItemBlock), BlockType.BulletedListItem)] - //[JsonSubtypes.KnownSubType(typeof(CalloutBlock), BlockType.Callout)] - //[JsonSubtypes.KnownSubType(typeof(ChildDatabaseBlock), BlockType.ChildDatabase)] - //[JsonSubtypes.KnownSubType(typeof(ChildPageBlock), BlockType.ChildPage)] - //[JsonSubtypes.KnownSubType(typeof(CodeBlock), BlockType.Code)] - //[JsonSubtypes.KnownSubType(typeof(DividerBlock), BlockType.Divider)] - //[JsonSubtypes.KnownSubType(typeof(EmbedBlock), BlockType.Embed)] - //[JsonSubtypes.KnownSubType(typeof(EquationBlock), BlockType.Equation)] - //[JsonSubtypes.KnownSubType(typeof(FileBlock), BlockType.File)] - //[JsonSubtypes.KnownSubType(typeof(HeadingOneBlock), BlockType.Heading_1)] - //[JsonSubtypes.KnownSubType(typeof(HeadingTwoBlock), BlockType.Heading_2)] - //[JsonSubtypes.KnownSubType(typeof(HeadingThreeeBlock), BlockType.Heading_3)] - //[JsonSubtypes.KnownSubType(typeof(ImageBlock), BlockType.Image)] - //[JsonSubtypes.KnownSubType(typeof(NumberedListItemBlock), BlockType.NumberedListItem)] - //[JsonSubtypes.KnownSubType(typeof(ParagraphBlock), BlockType.Paragraph)] - //[JsonSubtypes.KnownSubType(typeof(PDFBlock), BlockType.PDF)] - //[JsonSubtypes.KnownSubType(typeof(QuoteBlock), BlockType.Quote)] - //[JsonSubtypes.KnownSubType(typeof(TableOfContentsBlock), BlockType.TableOfContents)] - //[JsonSubtypes.KnownSubType(typeof(ToDoBlock), BlockType.ToDo)] - //[JsonSubtypes.KnownSubType(typeof(ToggleBlock), BlockType.Toggle)] - //[JsonSubtypes.KnownSubType(typeof(VideoBlock), BlockType.Video)] - //[JsonSubtypes.KnownSubType(typeof(UnsupportedBlock), BlockType.Unsupported)] - public interface IColumnChildrenBlock : IBlock + public interface ITemplateChildrenBlock : IBlock + { + } + + public interface ISyncedBlockChildren : IBlock + { + } + + public interface IColumnChildrenBlock : ITemplateChildrenBlock, ISyncedBlockChildren { } - //[JsonConverter(typeof(JsonSubtypes), "type")] - //[JsonSubtypes.KnownSubType(typeof(AudioBlock), BlockType.Audio)] - //[JsonSubtypes.KnownSubType(typeof(BookmarkBlock), BlockType.Bookmark)] - //[JsonSubtypes.KnownSubType(typeof(BreadcrumbBlock), BlockType.Breadcrumb)] - //[JsonSubtypes.KnownSubType(typeof(BulletedListItemBlock), BlockType.BulletedListItem)] - //[JsonSubtypes.KnownSubType(typeof(CalloutBlock), BlockType.Callout)] - //[JsonSubtypes.KnownSubType(typeof(ChildPageBlock), BlockType.ChildPage)] - //[JsonSubtypes.KnownSubType(typeof(ChildDatabaseBlock), BlockType.ChildDatabase)] - //[JsonSubtypes.KnownSubType(typeof(CodeBlock), BlockType.Code)] - //[JsonSubtypes.KnownSubType(typeof(ColumnListBlock), BlockType.ColumnList)] - //[JsonSubtypes.KnownSubType(typeof(DividerBlock), BlockType.Divider)] - //[JsonSubtypes.KnownSubType(typeof(EmbedBlock), BlockType.Embed)] - //[JsonSubtypes.KnownSubType(typeof(EquationBlock), BlockType.Equation)] - //[JsonSubtypes.KnownSubType(typeof(FileBlock), BlockType.File)] - //[JsonSubtypes.KnownSubType(typeof(HeadingOneBlock), BlockType.Heading_1)] - //[JsonSubtypes.KnownSubType(typeof(HeadingTwoBlock), BlockType.Heading_2)] - //[JsonSubtypes.KnownSubType(typeof(HeadingThreeeBlock), BlockType.Heading_3)] - //[JsonSubtypes.KnownSubType(typeof(ImageBlock), BlockType.Image)] - //[JsonSubtypes.KnownSubType(typeof(NumberedListItemBlock), BlockType.NumberedListItem)] - //[JsonSubtypes.KnownSubType(typeof(ParagraphBlock), BlockType.Paragraph)] - //[JsonSubtypes.KnownSubType(typeof(PDFBlock), BlockType.PDF)] - //[JsonSubtypes.KnownSubType(typeof(QuoteBlock), BlockType.Quote)] - //[JsonSubtypes.KnownSubType(typeof(TableOfContentsBlock), BlockType.TableOfContents)] - //[JsonSubtypes.KnownSubType(typeof(ToDoBlock), BlockType.ToDo)] - //[JsonSubtypes.KnownSubType(typeof(ToggleBlock), BlockType.Toggle)] - //[JsonSubtypes.KnownSubType(typeof(VideoBlock), BlockType.Video)] - //[JsonSubtypes.KnownSubType(typeof(UnsupportedBlock), BlockType.Unsupported)] public interface INonColumnBlock : IBlock { } diff --git a/Src/Notion.Client/Models/Blocks/ImageBlock.cs b/Src/Notion.Client/Models/Blocks/ImageBlock.cs index 17aa6bae..cd6b055f 100644 --- a/Src/Notion.Client/Models/Blocks/ImageBlock.cs +++ b/Src/Notion.Client/Models/Blocks/ImageBlock.cs @@ -4,9 +4,9 @@ namespace Notion.Client { public class ImageBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Image; - [JsonProperty("image")] public FileObject Image { get; set; } + + public override BlockType Type => BlockType.Image; } } diff --git a/Src/Notion.Client/Models/Blocks/LinkPreviewBlock.cs b/Src/Notion.Client/Models/Blocks/LinkPreviewBlock.cs new file mode 100644 index 00000000..72107e48 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/LinkPreviewBlock.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class LinkPreviewBlock : Block, IColumnChildrenBlock, INonColumnBlock + { + [JsonProperty("link_preview")] + public Data LinkPreview { get; set; } + + public override BlockType Type => BlockType.LinkPreview; + + public class Data + { + [JsonProperty("url")] + public string Url { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/LinkToPageBlock.cs b/Src/Notion.Client/Models/Blocks/LinkToPageBlock.cs new file mode 100644 index 00000000..ad688ca2 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/LinkToPageBlock.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class LinkToPageBlock : Block, IColumnChildrenBlock, INonColumnBlock + { + [JsonProperty("link_to_page")] + public IPageParent LinkToPage { get; set; } + + public override BlockType Type => BlockType.LinkToPage; + } +} diff --git a/Src/Notion.Client/Models/Blocks/NumberedListItemBlock.cs b/Src/Notion.Client/Models/Blocks/NumberedListItemBlock.cs index 90823f57..f4a645e9 100644 --- a/Src/Notion.Client/Models/Blocks/NumberedListItemBlock.cs +++ b/Src/Notion.Client/Models/Blocks/NumberedListItemBlock.cs @@ -1,19 +1,24 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { public class NumberedListItemBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.NumberedListItem; - [JsonProperty("numbered_list_item")] public Info NumberedListItem { get; set; } + public override BlockType Type => BlockType.NumberedListItem; + public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } [JsonProperty("children")] public IEnumerable Children { get; set; } diff --git a/Src/Notion.Client/Models/Blocks/PDFBlock.cs b/Src/Notion.Client/Models/Blocks/PDFBlock.cs index 85458796..6d7486d7 100644 --- a/Src/Notion.Client/Models/Blocks/PDFBlock.cs +++ b/Src/Notion.Client/Models/Blocks/PDFBlock.cs @@ -1,12 +1,14 @@ -using Newtonsoft.Json; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; namespace Notion.Client { + [SuppressMessage("ReSharper", "InconsistentNaming")] public class PDFBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.PDF; - [JsonProperty("pdf")] public FileObject PDF { get; set; } + + public override BlockType Type => BlockType.PDF; } } diff --git a/Src/Notion.Client/Models/Blocks/ParagraphBlock.cs b/Src/Notion.Client/Models/Blocks/ParagraphBlock.cs index 4a0b5d07..f3b73f36 100644 --- a/Src/Notion.Client/Models/Blocks/ParagraphBlock.cs +++ b/Src/Notion.Client/Models/Blocks/ParagraphBlock.cs @@ -1,19 +1,24 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { public class ParagraphBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Paragraph; - [JsonProperty("paragraph")] public Info Paragraph { get; set; } + public override BlockType Type => BlockType.Paragraph; + public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } [JsonProperty("children")] public IEnumerable Children { get; set; } diff --git a/Src/Notion.Client/Models/Blocks/QuoteBlock.cs b/Src/Notion.Client/Models/Blocks/QuoteBlock.cs index 3a4c6d9f..e1466be6 100644 --- a/Src/Notion.Client/Models/Blocks/QuoteBlock.cs +++ b/Src/Notion.Client/Models/Blocks/QuoteBlock.cs @@ -1,19 +1,24 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { public class QuoteBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Quote; - [JsonProperty("quote")] public Info Quote { get; set; } + public override BlockType Type => BlockType.Quote; + public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } [JsonProperty("children")] public IEnumerable Children { get; set; } diff --git a/Src/Notion.Client/Models/Blocks/Request/AudioBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/AudioBlockRequest.cs new file mode 100644 index 00000000..8378574e --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/AudioBlockRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class AudioBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("audio")] + public FileObject Audio { get; set; } + + public override BlockType Type => BlockType.Audio; + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/BlockObjectRequest.cs b/Src/Notion.Client/Models/Blocks/Request/BlockObjectRequest.cs new file mode 100644 index 00000000..a3eb3182 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/BlockObjectRequest.cs @@ -0,0 +1,28 @@ +using System; + +namespace Notion.Client +{ + public abstract class BlockObjectRequest : IBlockObjectRequest + { + public ObjectType Object => ObjectType.Block; + + public string Id { get; set; } + + public virtual BlockType Type { get; set; } + + public DateTime CreatedTime { get; set; } + + public DateTime LastEditedTime { get; set; } + + public virtual bool HasChildren { get; set; } + + public PartialUser CreatedBy { get; set; } + + public PartialUser LastEditedBy { get; set; } + + /// + /// Information about the block's parent. + /// + public IBlockParent Parent { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/BookmarkBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/BookmarkBlockRequest.cs new file mode 100644 index 00000000..0c7c0be2 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/BookmarkBlockRequest.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class BookmarkBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("bookmark")] + public Info Bookmark { get; set; } + + public override BlockType Type => BlockType.Bookmark; + + public class Info + { + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/BreadcrumbBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/BreadcrumbBlockRequest.cs new file mode 100644 index 00000000..f6039456 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/BreadcrumbBlockRequest.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class BreadcrumbBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("breadcrumb")] + public Data Breadcrumb { get; set; } + + public override BlockType Type => BlockType.Breadcrumb; + + public class Data + { + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/BulletedListItemBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/BulletedListItemBlockRequest.cs new file mode 100644 index 00000000..0aa8a183 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/BulletedListItemBlockRequest.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class BulletedListItemBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("bulleted_list_item")] + public Info BulletedListItem { get; set; } + + public override BlockType Type => BlockType.BulletedListItem; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/CalloutBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/CalloutBlockRequest.cs new file mode 100644 index 00000000..b8a56823 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/CalloutBlockRequest.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class CalloutBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("callout")] + public Info Callout { get; set; } + + public override BlockType Type => BlockType.Callout; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("icon")] + public IPageIcon Icon { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ChildDatabaseBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ChildDatabaseBlockRequest.cs new file mode 100644 index 00000000..34ff9387 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ChildDatabaseBlockRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ChildDatabaseBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("child_database")] + public Info ChildDatabase { get; set; } + + public override BlockType Type => BlockType.ChildDatabase; + + public class Info + { + [JsonProperty("title")] + public string Title { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ChildPageBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ChildPageBlockRequest.cs new file mode 100644 index 00000000..43213b77 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ChildPageBlockRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ChildPageBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("child_page")] + public Info ChildPage { get; set; } + + public override BlockType Type => BlockType.ChildPage; + + public class Info + { + [JsonProperty("title")] + public string Title { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/CodeBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/CodeBlockRequest.cs new file mode 100644 index 00000000..732e3208 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/CodeBlockRequest.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class CodeBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("code")] + public Info Code { get; set; } + + public override BlockType Type => BlockType.Code; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("language")] + public string Language { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ColumnBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ColumnBlockRequest.cs new file mode 100644 index 00000000..c4136294 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ColumnBlockRequest.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ColumnBlockRequest : BlockObjectRequest + { + public override BlockType Type => BlockType.Column; + + [JsonProperty("column")] + public Info Column { get; set; } + + public class Info + { + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ColumnListBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ColumnListBlockRequest.cs new file mode 100644 index 00000000..431acf8e --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ColumnListBlockRequest.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ColumnListBlockRequest : BlockObjectRequest, INonColumnBlockRequest + { + [JsonProperty("column_list")] + public Info ColumnList { get; set; } + + public override BlockType Type => BlockType.ColumnList; + + public class Info + { + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/DividerBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/DividerBlockRequest.cs new file mode 100644 index 00000000..49c81059 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/DividerBlockRequest.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class DividerBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("divider")] + public Data Divider { get; set; } + + public override BlockType Type => BlockType.Divider; + + public class Data + { + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/EmbedBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/EmbedBlockRequest.cs new file mode 100644 index 00000000..81cb2339 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/EmbedBlockRequest.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class EmbedBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("embed")] + public Info Embed { get; set; } + + public override BlockType Type => BlockType.Embed; + + public class Info + { + [JsonProperty("url")] + public string Url { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/EquationBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/EquationBlockRequest.cs new file mode 100644 index 00000000..5c2ddb8a --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/EquationBlockRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class EquationBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("equation")] + public Info Equation { get; set; } + + public override BlockType Type => BlockType.Equation; + + public class Info + { + [JsonProperty("expression")] + public string Expression { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/FileBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/FileBlockRequest.cs new file mode 100644 index 00000000..0539c41c --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/FileBlockRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class FileBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("file")] + public FileObject File { get; set; } + + public override BlockType Type => BlockType.File; + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/HeadingOneBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/HeadingOneBlockRequest.cs new file mode 100644 index 00000000..dd91451a --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/HeadingOneBlockRequest.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class HeadingOneBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("heading_1")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public Info Heading_1 { get; set; } + + public override BlockType Type => BlockType.Heading_1; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("is_toggleable")] + public bool IsToggleable { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/HeadingThreeBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/HeadingThreeBlockRequest.cs new file mode 100644 index 00000000..9b78c80e --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/HeadingThreeBlockRequest.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class HeadingThreeBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("heading_3")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public Info Heading_3 { get; set; } + + public override BlockType Type => BlockType.Heading_3; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("is_toggleable")] + public bool IsToggleable { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/HeadingTwoBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/HeadingTwoBlockRequest.cs new file mode 100644 index 00000000..dfdeb9dd --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/HeadingTwoBlockRequest.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class HeadingTwoBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("heading_2")] + [SuppressMessage("ReSharper", "InconsistentNaming")] + public Info Heading_2 { get; set; } + + public override BlockType Type => BlockType.Heading_2; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("is_toggleable")] + public bool IsToggleable { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/IBlockObjectRequest.cs b/Src/Notion.Client/Models/Blocks/Request/IBlockObjectRequest.cs new file mode 100644 index 00000000..b3fbaf55 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/IBlockObjectRequest.cs @@ -0,0 +1,19 @@ +using JsonSubTypes; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public interface IBlockObjectRequest : IObject, IObjectModificationData + { + [JsonProperty("type")] + [JsonConverter(typeof(StringEnumConverter))] + BlockType Type { get; } + + [JsonProperty("has_children")] + bool HasChildren { get; set; } + + [JsonProperty("parent")] + IBlockParent Parent { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/IColumnChildrenBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/IColumnChildrenBlockRequest.cs new file mode 100644 index 00000000..54e08fab --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/IColumnChildrenBlockRequest.cs @@ -0,0 +1,18 @@ +namespace Notion.Client +{ + public interface ITemplateChildrenBlockRequest : IBlockObjectRequest + { + } + + public interface ISyncedBlockChildrenRequest : IBlockObjectRequest + { + } + + public interface IColumnChildrenBlockRequest : ITemplateChildrenBlockRequest, ISyncedBlockChildrenRequest + { + } + + public interface INonColumnBlockRequest : IBlockObjectRequest + { + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ImageBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ImageBlockRequest.cs new file mode 100644 index 00000000..d3ec252b --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ImageBlockRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ImageBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("image")] + public FileObject Image { get; set; } + + public override BlockType Type => BlockType.Image; + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/LinkPreviewBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/LinkPreviewBlockRequest.cs new file mode 100644 index 00000000..11a47592 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/LinkPreviewBlockRequest.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class LinkPreviewBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("link_preview")] + public Data LinkPreview { get; set; } + + public override BlockType Type => BlockType.LinkPreview; + + public class Data + { + [JsonProperty("url")] + public string Url { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/LinkToPageBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/LinkToPageBlockRequest.cs new file mode 100644 index 00000000..ec182df5 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/LinkToPageBlockRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class LinkToPageBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("link_to_page")] + public IPageParent LinkToPage { get; set; } + + public override BlockType Type => BlockType.LinkToPage; + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/NumberedListItemBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/NumberedListItemBlockRequest.cs new file mode 100644 index 00000000..87342c1d --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/NumberedListItemBlockRequest.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class NumberedListItemBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("numbered_list_item")] + public Info NumberedListItem { get; set; } + + public override BlockType Type => BlockType.NumberedListItem; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/PDFBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/PDFBlockRequest.cs new file mode 100644 index 00000000..5590000d --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/PDFBlockRequest.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; + +namespace Notion.Client +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + public class PDFBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("pdf")] + public FileObject PDF { get; set; } + + public override BlockType Type => BlockType.PDF; + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ParagraphBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ParagraphBlockRequest.cs new file mode 100644 index 00000000..8e8dfa51 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ParagraphBlockRequest.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class ParagraphBlockRequest : Block, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("paragraph")] + public Info Paragraph { get; set; } + + public override BlockType Type => BlockType.Paragraph; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/QuoteBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/QuoteBlockRequest.cs new file mode 100644 index 00000000..3acd3130 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/QuoteBlockRequest.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class QuoteBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("quote")] + public Info Quote { get; set; } + + public override BlockType Type => BlockType.Quote; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/SyncedBlockBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/SyncedBlockBlockRequest.cs new file mode 100644 index 00000000..5338e5bb --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/SyncedBlockBlockRequest.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class SyncedBlockBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("synced_block")] + public Data SyncedBlock { get; set; } + + public override BlockType Type => BlockType.SyncedBlock; + + public class Data + { + [JsonProperty("synced_from")] + public SyncedFromBlockId SyncedFrom { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + + public class SyncedFromBlockId + { + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("block_id")] + public string BlockId { get; set; } + } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/TableBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/TableBlockRequest.cs new file mode 100644 index 00000000..1e966182 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/TableBlockRequest.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TableBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("table")] + public Info Table { get; set; } + + public override BlockType Type => BlockType.Table; + + public class Info + { + [JsonProperty("table_width")] + public int TableWidth { get; set; } + + [JsonProperty("has_column_header")] + public bool HasColumnHeader { get; set; } + + [JsonProperty("has_row_header")] + public bool HasRowHeader { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/TableOfContentsBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/TableOfContentsBlockRequest.cs new file mode 100644 index 00000000..2aef499d --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/TableOfContentsBlockRequest.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class TableOfContentsBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("table_of_contents")] + public Data TableOfContents { get; set; } + + public override BlockType Type => BlockType.TableOfContents; + + public class Data + { + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/TableRowBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/TableRowBlockRequest.cs new file mode 100644 index 00000000..ead85b33 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/TableRowBlockRequest.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TableRowBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("table_row")] + public Info TableRow { get; set; } + + public override BlockType Type => BlockType.TableRow; + + public class Info + { + [JsonProperty("cells")] + public IEnumerable> Cells { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/TemplateBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/TemplateBlockRequest.cs new file mode 100644 index 00000000..04c161d2 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/TemplateBlockRequest.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TemplateBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("template")] + public Data Template { get; set; } + + public override BlockType Type => BlockType.Template; + + public class Data + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ToDoBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ToDoBlockRequest.cs new file mode 100644 index 00000000..ed7b09c3 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ToDoBlockRequest.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class ToDoBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("to_do")] + public Info ToDo { get; set; } + + public override BlockType Type => BlockType.ToDo; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("checked")] + public bool IsChecked { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/ToggleBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/ToggleBlockRequest.cs new file mode 100644 index 00000000..fbbbcd37 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/ToggleBlockRequest.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class ToggleBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("toggle")] + public Info Toggle { get; set; } + + public override BlockType Type => BlockType.Toggle; + + public class Info + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/Request/VideoBlockRequest.cs b/Src/Notion.Client/Models/Blocks/Request/VideoBlockRequest.cs new file mode 100644 index 00000000..238b2d26 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/Request/VideoBlockRequest.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class VideoBlockRequest : BlockObjectRequest, IColumnChildrenBlockRequest, INonColumnBlockRequest + { + [JsonProperty("video")] + public FileObject Video { get; set; } + + public override BlockType Type => BlockType.Video; + } +} diff --git a/Src/Notion.Client/Models/Blocks/SyncedBlockBlock.cs b/Src/Notion.Client/Models/Blocks/SyncedBlockBlock.cs new file mode 100644 index 00000000..98aa1d0f --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/SyncedBlockBlock.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class SyncedBlockBlock : Block, IColumnChildrenBlock, INonColumnBlock + { + [JsonProperty("synced_block")] + public Data SyncedBlock { get; set; } + + public override BlockType Type => BlockType.SyncedBlock; + + public class Data + { + [JsonProperty("synced_from")] + public SyncedFromBlockId SyncedFrom { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + + public class SyncedFromBlockId + { + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("block_id")] + public string BlockId { get; set; } + } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/TableBlock.cs b/Src/Notion.Client/Models/Blocks/TableBlock.cs new file mode 100644 index 00000000..80fe72bc --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/TableBlock.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TableBlock : Block, IColumnChildrenBlock, INonColumnBlock + { + [JsonProperty("table")] + public Info Table { get; set; } + + public override BlockType Type => BlockType.Table; + + public class Info + { + [JsonProperty("table_width")] + public int TableWidth { get; set; } + + [JsonProperty("has_column_header")] + public bool HasColumnHeader { get; set; } + + [JsonProperty("has_row_header")] + public bool HasRowHeader { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/TableOfContentsBlock.cs b/Src/Notion.Client/Models/Blocks/TableOfContentsBlock.cs index 49e11bf4..0a6c9f26 100644 --- a/Src/Notion.Client/Models/Blocks/TableOfContentsBlock.cs +++ b/Src/Notion.Client/Models/Blocks/TableOfContentsBlock.cs @@ -1,16 +1,20 @@ using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { public class TableOfContentsBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.TableOfContents; - [JsonProperty("table_of_contents")] public Data TableOfContents { get; set; } + public override BlockType Type => BlockType.TableOfContents; + public class Data { + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } } } } diff --git a/Src/Notion.Client/Models/Blocks/TableRowBlock.cs b/Src/Notion.Client/Models/Blocks/TableRowBlock.cs new file mode 100644 index 00000000..be105447 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/TableRowBlock.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TableRowBlock : Block, IColumnChildrenBlock, INonColumnBlock + { + [JsonProperty("table_row")] + public Info TableRow { get; set; } + + public override BlockType Type => BlockType.TableRow; + + public class Info + { + [JsonProperty("cells")] + public IEnumerable> Cells { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/TemplateBlock.cs b/Src/Notion.Client/Models/Blocks/TemplateBlock.cs new file mode 100644 index 00000000..867e7c85 --- /dev/null +++ b/Src/Notion.Client/Models/Blocks/TemplateBlock.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TemplateBlock : Block, IColumnChildrenBlock, INonColumnBlock + { + [JsonProperty("template")] + public Data Template { get; set; } + + public override BlockType Type => BlockType.Template; + + public class Data + { + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("children")] + public IEnumerable Children { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Blocks/ToDoBlock.cs b/Src/Notion.Client/Models/Blocks/ToDoBlock.cs index 2d033c95..0c52201a 100644 --- a/Src/Notion.Client/Models/Blocks/ToDoBlock.cs +++ b/Src/Notion.Client/Models/Blocks/ToDoBlock.cs @@ -1,23 +1,28 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { public class ToDoBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.ToDo; - [JsonProperty("to_do")] public Info ToDo { get; set; } + public override BlockType Type => BlockType.ToDo; + public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } [JsonProperty("checked")] public bool IsChecked { get; set; } + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } + [JsonProperty("children")] public IEnumerable Children { get; set; } } diff --git a/Src/Notion.Client/Models/Blocks/ToggleBlock.cs b/Src/Notion.Client/Models/Blocks/ToggleBlock.cs index 95eef646..f61a1368 100644 --- a/Src/Notion.Client/Models/Blocks/ToggleBlock.cs +++ b/Src/Notion.Client/Models/Blocks/ToggleBlock.cs @@ -1,19 +1,24 @@ using System.Collections.Generic; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { public class ToggleBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Toggle; - [JsonProperty("toggle")] public Info Toggle { get; set; } + public override BlockType Type => BlockType.Toggle; + public class Info { - [JsonProperty("text")] - public IEnumerable Text { get; set; } + [JsonProperty("rich_text")] + public IEnumerable RichText { get; set; } + + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } [JsonProperty("children")] public IEnumerable Children { get; set; } diff --git a/Src/Notion.Client/Models/Blocks/VideoBlock.cs b/Src/Notion.Client/Models/Blocks/VideoBlock.cs index 015bed96..7713ff7b 100644 --- a/Src/Notion.Client/Models/Blocks/VideoBlock.cs +++ b/Src/Notion.Client/Models/Blocks/VideoBlock.cs @@ -4,9 +4,9 @@ namespace Notion.Client { public class VideoBlock : Block, IColumnChildrenBlock, INonColumnBlock { - public override BlockType Type => BlockType.Video; - [JsonProperty("video")] public FileObject Video { get; set; } + + public override BlockType Type => BlockType.Video; } } diff --git a/Src/Notion.Client/Models/Common/IObjectModificationData.cs b/Src/Notion.Client/Models/Common/IObjectModificationData.cs new file mode 100644 index 00000000..0e70ffcf --- /dev/null +++ b/Src/Notion.Client/Models/Common/IObjectModificationData.cs @@ -0,0 +1,32 @@ +using System; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public interface IObjectModificationData + { + /// + /// Date and time when this object was created. + /// + [JsonProperty("created_time")] + DateTime CreatedTime { get; set; } + + /// + /// Date and time when this object was updated. + /// + [JsonProperty("last_edited_time")] + DateTime LastEditedTime { get; set; } + + /// + /// User who created the object. + /// + [JsonProperty("created_by")] + PartialUser CreatedBy { get; set; } + + /// + /// User who last modified the object. + /// + [JsonProperty("last_edited_by")] + PartialUser LastEditedBy { get; set; } + } +} diff --git a/Src/Notion.Client/Models/CustomEmoji.cs b/Src/Notion.Client/Models/CustomEmoji.cs new file mode 100644 index 00000000..0f7a4c48 --- /dev/null +++ b/Src/Notion.Client/Models/CustomEmoji.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class CustomEmoji + { + [JsonProperty("id")] + public string Id { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("url")] + public string Url { get; set; } + } +} diff --git a/Src/Notion.Client/Models/CustomEmojiObject.cs b/Src/Notion.Client/Models/CustomEmojiObject.cs new file mode 100644 index 00000000..1faa4d74 --- /dev/null +++ b/Src/Notion.Client/Models/CustomEmojiObject.cs @@ -0,0 +1,13 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class CustomEmojiObject : IPageIcon + { + [JsonProperty("custom_emoji")] + public CustomEmoji CustomEmoji { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Database/Database.cs b/Src/Notion.Client/Models/Database/Database.cs index 6943048f..be288ea0 100644 --- a/Src/Notion.Client/Models/Database/Database.cs +++ b/Src/Notion.Client/Models/Database/Database.cs @@ -4,18 +4,8 @@ namespace Notion.Client { - public class Database : IObject + public class Database : IObject, IObjectModificationData, IWikiDatabase { - public ObjectType Object => ObjectType.Database; - - public string Id { get; set; } - - [JsonProperty("created_time")] - public DateTime CreatedTime { get; set; } - - [JsonProperty("last_edited_time")] - public DateTime LastEditedTime { get; set; } - [JsonProperty("title")] public List Title { get; set; } @@ -32,9 +22,38 @@ public class Database : IObject public FileObject Cover { get; set; } /// - /// The URL of the Notion database. + /// The URL of the Notion database. /// [JsonProperty("url")] public string Url { get; set; } + + [JsonProperty("in_trash")] + public bool InTrash { get; set; } + + [JsonProperty("is_inline")] + public bool IsInline { get; set; } + + [JsonProperty("description")] + public IEnumerable Description { get; set; } + + public ObjectType Object => ObjectType.Database; + + public string Id { get; set; } + + [JsonProperty("created_time")] + public DateTime CreatedTime { get; set; } + + [JsonProperty("last_edited_time")] + public DateTime LastEditedTime { get; set; } + + public PartialUser CreatedBy { get; set; } + + public PartialUser LastEditedBy { get; set; } + + /// + /// The public page URL if the page has been published to the web. Otherwise, null. + /// + [JsonProperty("public_url")] + public string PublicUrl { get; set; } } } diff --git a/Src/Notion.Client/Models/Database/IDatabaseParent.cs b/Src/Notion.Client/Models/Database/IDatabaseParent.cs index bc4b2703..768f815b 100644 --- a/Src/Notion.Client/Models/Database/IDatabaseParent.cs +++ b/Src/Notion.Client/Models/Database/IDatabaseParent.cs @@ -4,8 +4,9 @@ namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(PageParent), ParentType.PageId)] - [JsonSubtypes.KnownSubType(typeof(WorkspaceParent), ParentType.Workspace)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(PageParent), ParentType.PageId)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(WorkspaceParent), ParentType.Workspace)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(BlockParent), ParentType.BlockId)] public interface IDatabaseParent : IParent { } diff --git a/Src/Notion.Client/Models/Database/IWikiDatabase.cs b/Src/Notion.Client/Models/Database/IWikiDatabase.cs new file mode 100644 index 00000000..59969175 --- /dev/null +++ b/Src/Notion.Client/Models/Database/IWikiDatabase.cs @@ -0,0 +1,12 @@ +using JsonSubTypes; +using Newtonsoft.Json; + +namespace Notion.Client +{ + [JsonConverter(typeof(JsonSubtypes), "object")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(Page), ObjectType.Page)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(Database), ObjectType.Database)] + public interface IWikiDatabase : IObject + { + } +} diff --git a/Src/Notion.Client/Models/Database/Properties/ButtonProperty.cs b/Src/Notion.Client/Models/Database/Properties/ButtonProperty.cs new file mode 100644 index 00000000..983138e2 --- /dev/null +++ b/Src/Notion.Client/Models/Database/Properties/ButtonProperty.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; +using System.Threading.Tasks; + +namespace Notion.Client +{ + public class ButtonProperty : Property + { + public override PropertyType Type => PropertyType.Button; + + [JsonProperty("button")] + public Dictionary Button { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Database/Properties/NumberProperty.cs b/Src/Notion.Client/Models/Database/Properties/NumberProperty.cs index a00d7d9b..f50e11a2 100644 --- a/Src/Notion.Client/Models/Database/Properties/NumberProperty.cs +++ b/Src/Notion.Client/Models/Database/Properties/NumberProperty.cs @@ -1,6 +1,4 @@ -using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using Newtonsoft.Json; namespace Notion.Client { @@ -15,106 +13,6 @@ public class NumberProperty : Property public class Number { [JsonProperty("format")] - [JsonConverter(typeof(StringEnumConverter))] - public NumberFormat Format { get; set; } - } - - public enum NumberFormat - { - [EnumMember(Value = null)] - Unknown, - - [EnumMember(Value = "number")] - Number, - - [EnumMember(Value = "number_with_commas")] - NumberWithCommas, - - [EnumMember(Value = "percent")] - Percent, - - [EnumMember(Value = "dollar")] - Dollar, - - [EnumMember(Value = "euro")] - Euro, - - [EnumMember(Value = "pound")] - Pound, - - [EnumMember(Value = "yen")] - Yen, - - [EnumMember(Value = "ruble")] - Ruble, - - [EnumMember(Value = "rupee")] - Rupee, - - [EnumMember(Value = "won")] - Won, - - [EnumMember(Value = "yuan")] - Yuan, - - [EnumMember(Value = "hong_kong_dollar")] - HongKongDollar, - - [EnumMember(Value = "new_zealand_dollar")] - NewZealandDollar, - - [EnumMember(Value = "krona")] - Krona, - - [EnumMember(Value = "norwegian_krone")] - NorwegianKrone, - - [EnumMember(Value = "mexican_peso")] - MexicanPeso, - - [EnumMember(Value = "rand")] - Rand, - - [EnumMember(Value = "new_taiwan_dollar")] - NewTaiwanDollar, - - [EnumMember(Value = "danish_krone")] - DanishKrone, - - [EnumMember(Value = "zloty")] - Zloty, - - [EnumMember(Value = "baht")] - Baht, - - [EnumMember(Value = "forint")] - Forint, - - [EnumMember(Value = "koruna")] - Koruna, - - [EnumMember(Value = "shekel")] - Shekel, - - [EnumMember(Value = "chilean_peso")] - ChileanPeso, - - [EnumMember(Value = "philippine_peso")] - PhilippinePeso, - - [EnumMember(Value = "dirham")] - Dirham, - - [EnumMember(Value = "colombian_peso")] - ColombianPeso, - - [EnumMember(Value = "riyal")] - Riyal, - - [EnumMember(Value = "ringgit")] - Ringgit, - - [EnumMember(Value = "leu")] - Leu + public string Format { get; set; } } } diff --git a/Src/Notion.Client/Models/Database/Properties/Property.cs b/Src/Notion.Client/Models/Database/Properties/Property.cs index 30ecd151..6392bbaf 100644 --- a/Src/Notion.Client/Models/Database/Properties/Property.cs +++ b/Src/Notion.Client/Models/Database/Properties/Property.cs @@ -5,25 +5,28 @@ namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(CheckboxProperty), PropertyType.Checkbox)] - [JsonSubtypes.KnownSubType(typeof(CreatedByProperty), PropertyType.CreatedBy)] - [JsonSubtypes.KnownSubType(typeof(CreatedTimeProperty), PropertyType.CreatedTime)] - [JsonSubtypes.KnownSubType(typeof(DateProperty), PropertyType.Date)] - [JsonSubtypes.KnownSubType(typeof(EmailProperty), PropertyType.Email)] - [JsonSubtypes.KnownSubType(typeof(FilesProperty), PropertyType.Files)] - [JsonSubtypes.KnownSubType(typeof(FormulaProperty), PropertyType.Formula)] - [JsonSubtypes.KnownSubType(typeof(LastEditedByProperty), PropertyType.LastEditedBy)] - [JsonSubtypes.KnownSubType(typeof(LastEditedTimeProperty), PropertyType.LastEditedTime)] - [JsonSubtypes.KnownSubType(typeof(MultiSelectProperty), PropertyType.MultiSelect)] - [JsonSubtypes.KnownSubType(typeof(NumberProperty), PropertyType.Number)] - [JsonSubtypes.KnownSubType(typeof(PeopleProperty), PropertyType.People)] - [JsonSubtypes.KnownSubType(typeof(PhoneNumberProperty), PropertyType.PhoneNumber)] - [JsonSubtypes.KnownSubType(typeof(RelationProperty), PropertyType.Relation)] - [JsonSubtypes.KnownSubType(typeof(RichTextProperty), PropertyType.RichText)] - [JsonSubtypes.KnownSubType(typeof(RollupProperty), PropertyType.Rollup)] - [JsonSubtypes.KnownSubType(typeof(SelectProperty), PropertyType.Select)] - [JsonSubtypes.KnownSubType(typeof(TitleProperty), PropertyType.Title)] - [JsonSubtypes.KnownSubType(typeof(UrlProperty), PropertyType.Url)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CheckboxProperty), PropertyType.Checkbox)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CreatedByProperty), PropertyType.CreatedBy)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CreatedTimeProperty), PropertyType.CreatedTime)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(DateProperty), PropertyType.Date)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(EmailProperty), PropertyType.Email)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(FilesProperty), PropertyType.Files)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(FormulaProperty), PropertyType.Formula)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(LastEditedByProperty), PropertyType.LastEditedBy)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(LastEditedTimeProperty), PropertyType.LastEditedTime)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(MultiSelectProperty), PropertyType.MultiSelect)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(NumberProperty), PropertyType.Number)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(PeopleProperty), PropertyType.People)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(PhoneNumberProperty), PropertyType.PhoneNumber)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RelationProperty), PropertyType.Relation)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RichTextProperty), PropertyType.RichText)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RollupProperty), PropertyType.Rollup)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(SelectProperty), PropertyType.Select)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(StatusProperty), PropertyType.Status)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(TitleProperty), PropertyType.Title)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(UrlProperty), PropertyType.Url)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(UniqueIdProperty), PropertyType.UniqueId)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ButtonProperty), PropertyType.Button)] public class Property { [JsonProperty("id")] diff --git a/Src/Notion.Client/Models/Database/Properties/PropertyType.cs b/Src/Notion.Client/Models/Database/Properties/PropertyType.cs index bbc9eba7..206f9fe2 100644 --- a/Src/Notion.Client/Models/Database/Properties/PropertyType.cs +++ b/Src/Notion.Client/Models/Database/Properties/PropertyType.cs @@ -1,7 +1,9 @@ -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMember.Global")] public enum PropertyType { [EnumMember(Value = null)] @@ -62,6 +64,15 @@ public enum PropertyType LastEditedBy, [EnumMember(Value = "last_edited_time")] - LastEditedTime + LastEditedTime, + + [EnumMember(Value = "status")] + Status, + + [EnumMember(Value = "unique_id")] + UniqueId, + + [EnumMember(Value = "button")] + Button, } } diff --git a/Src/Notion.Client/Models/Database/Properties/RelationProperty.cs b/Src/Notion.Client/Models/Database/Properties/RelationProperty.cs deleted file mode 100644 index 8c55bc80..00000000 --- a/Src/Notion.Client/Models/Database/Properties/RelationProperty.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Newtonsoft.Json; - -namespace Notion.Client -{ - public class RelationProperty : Property - { - public override PropertyType Type => PropertyType.Relation; - - - [JsonProperty("relation")] - public Relation Relation { get; set; } - } - - public class Relation - { - [JsonProperty("datebase_id")] - public string DatabaseId { get; set; } - - [JsonProperty("synced_property_name")] - public string SyncedPropertyName { get; set; } - - [JsonProperty("synced_property_id")] - public string SyncedPropertyId { get; set; } - } -} diff --git a/Src/Notion.Client/Models/Database/Properties/RelationProperty/DualPropertyRelation.cs b/Src/Notion.Client/Models/Database/Properties/RelationProperty/DualPropertyRelation.cs new file mode 100644 index 00000000..f1c8c1ec --- /dev/null +++ b/Src/Notion.Client/Models/Database/Properties/RelationProperty/DualPropertyRelation.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class DualPropertyRelation : RelationData + { + public override RelationType Type => RelationType.Dual; + + [JsonProperty("dual_property")] + public Data DualProperty { get; set; } + + public class Data + { + [JsonProperty("synced_property_name")] + public string SyncedPropertyName { get; set; } + + [JsonProperty("synced_property_id")] + public string SyncedPropertyId { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Database/Properties/RelationProperty/RelationData.cs b/Src/Notion.Client/Models/Database/Properties/RelationProperty/RelationData.cs new file mode 100644 index 00000000..d129acc6 --- /dev/null +++ b/Src/Notion.Client/Models/Database/Properties/RelationProperty/RelationData.cs @@ -0,0 +1,19 @@ +using JsonSubTypes; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + [JsonConverter(typeof(JsonSubtypes), "type")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(SinglePropertyRelation), RelationType.Single)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(DualPropertyRelation), RelationType.Dual)] + public abstract class RelationData + { + [JsonProperty("database_id")] + public string DatabaseId { get; set; } + + [JsonProperty("type")] + [JsonConverter(typeof(StringEnumConverter))] + public virtual RelationType Type { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Database/Properties/RelationProperty/RelationProperty.cs b/Src/Notion.Client/Models/Database/Properties/RelationProperty/RelationProperty.cs new file mode 100644 index 00000000..382e7088 --- /dev/null +++ b/Src/Notion.Client/Models/Database/Properties/RelationProperty/RelationProperty.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class RelationProperty : Property + { + public override PropertyType Type => PropertyType.Relation; + + [JsonProperty("relation")] + public RelationData Relation { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Database/Properties/RelationProperty/RelationType.cs b/Src/Notion.Client/Models/Database/Properties/RelationProperty/RelationType.cs new file mode 100644 index 00000000..89ce1c56 --- /dev/null +++ b/Src/Notion.Client/Models/Database/Properties/RelationProperty/RelationType.cs @@ -0,0 +1,13 @@ +using System.Runtime.Serialization; + +namespace Notion.Client +{ + public enum RelationType + { + [EnumMember(Value = "single_property")] + Single, + + [EnumMember(Value = "dual_property")] + Dual + } +} diff --git a/Src/Notion.Client/Models/Database/Properties/RelationProperty/SinglePropertyRelation.cs b/Src/Notion.Client/Models/Database/Properties/RelationProperty/SinglePropertyRelation.cs new file mode 100644 index 00000000..9ba8ae12 --- /dev/null +++ b/Src/Notion.Client/Models/Database/Properties/RelationProperty/SinglePropertyRelation.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class SinglePropertyRelation : RelationData + { + public override RelationType Type => RelationType.Single; + + [JsonProperty("single_property")] + public Dictionary SingleProperty { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Database/Properties/RollupProperty.cs b/Src/Notion.Client/Models/Database/Properties/RollupProperty.cs index 5176dd88..8ce88b15 100644 --- a/Src/Notion.Client/Models/Database/Properties/RollupProperty.cs +++ b/Src/Notion.Client/Models/Database/Properties/RollupProperty.cs @@ -1,6 +1,4 @@ -using System.Runtime.Serialization; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; +using Newtonsoft.Json; namespace Notion.Client { @@ -27,57 +25,6 @@ public class Rollup public string RollupPropertyId { get; set; } [JsonProperty("function")] - [JsonConverter(typeof(StringEnumConverter))] - public Function Function { get; set; } - } - - public enum Function - { - [EnumMember(Value = null)] - Unknown, - - [EnumMember(Value = "count_all")] - CountAll, - [EnumMember(Value = "count_values")] - CountValues, - - [EnumMember(Value = "count_unique_values")] - CountUniqueValues, - - [EnumMember(Value = "count_empty")] - CountEmpty, - - [EnumMember(Value = "count_not_empty")] - CountNotEmpty, - - [EnumMember(Value = "percent_empty")] - PercentEmpty, - - [EnumMember(Value = "percent_not_empty")] - PercentNotEmpty, - - [EnumMember(Value = "sum")] - Sum, - - [EnumMember(Value = "average")] - Average, - - [EnumMember(Value = "median")] - Median, - - [EnumMember(Value = "min")] - Min, - - [EnumMember(Value = "max")] - Max, - - [EnumMember(Value = "range")] - Range, - - [EnumMember(Value = "show_original")] - ShowOriginal, - - [EnumMember(Value = "show_unique")] - ShowUnique + public string Function { get; set; } } } diff --git a/Src/Notion.Client/Models/Database/Properties/SelectProperty.cs b/Src/Notion.Client/Models/Database/Properties/SelectProperty.cs index 4aaa0f06..f9675443 100644 --- a/Src/Notion.Client/Models/Database/Properties/SelectProperty.cs +++ b/Src/Notion.Client/Models/Database/Properties/SelectProperty.cs @@ -1,13 +1,15 @@ using System.Collections.Generic; -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")] public class SelectProperty : Property { public override PropertyType Type => PropertyType.Select; + public OptionWrapper Select { get; set; } } @@ -20,61 +22,26 @@ public class OptionWrapper public class SelectOption { /// - /// Name of the option as it appears in Notion. + /// Name of the option as it appears in Notion. /// [JsonProperty("name")] public string Name { get; set; } /// - /// ID of the option. + /// ID of the option. /// [JsonProperty("id")] public string Id { get; set; } /// - /// Color of the option. Possible values are: "default", "gray", "brown", "red", "orange", "yellow", "green", "blue", "purple", "pink". Defaults to "default". + /// Color of the option. Possible values are: "default", "gray", "brown", "red", "orange", "yellow", "green", "blue", + /// "purple", "pink". Defaults to "default". /// [JsonProperty("color")] [JsonConverter(typeof(StringEnumConverter))] public Color? Color { get; set; } } - public enum Color - { - [EnumMember(Value = null)] - Unknown, - - [EnumMember(Value = "default")] - Default, - - [EnumMember(Value = "gray")] - Gray, - - [EnumMember(Value = "brown")] - Brown, - - [EnumMember(Value = "orange")] - Orange, - - [EnumMember(Value = "yellow")] - Yellow, - - [EnumMember(Value = "green")] - Green, - - [EnumMember(Value = "blue")] - Blue, - - [EnumMember(Value = "purple")] - Purple, - - [EnumMember(Value = "pink")] - Pink, - - [EnumMember(Value = "red")] - Red - } - public class MultiSelectProperty : Property { public override PropertyType Type => PropertyType.MultiSelect; diff --git a/Src/Notion.Client/Models/Database/Properties/StatusProperty.cs b/Src/Notion.Client/Models/Database/Properties/StatusProperty.cs new file mode 100644 index 00000000..92ec46fd --- /dev/null +++ b/Src/Notion.Client/Models/Database/Properties/StatusProperty.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class StatusProperty : Property + { + public override PropertyType Type => PropertyType.Status; + + [JsonProperty("status")] + public Dictionary Status { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Database/Properties/UniqueIdProperty.cs b/Src/Notion.Client/Models/Database/Properties/UniqueIdProperty.cs new file mode 100644 index 00000000..4a9e9d5d --- /dev/null +++ b/Src/Notion.Client/Models/Database/Properties/UniqueIdProperty.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class UniqueIdProperty : Property + { + public override PropertyType Type => PropertyType.UniqueId; + + [JsonProperty("unique_id")] + public Dictionary UniqueId { get; set; } + } +} + diff --git a/Src/Notion.Client/Models/Database/RichText/RichTextBase.cs b/Src/Notion.Client/Models/Database/RichText/RichTextBase.cs index 1c9a7610..3f10d578 100644 --- a/Src/Notion.Client/Models/Database/RichText/RichTextBase.cs +++ b/Src/Notion.Client/Models/Database/RichText/RichTextBase.cs @@ -5,9 +5,9 @@ namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(RichTextText), RichTextType.Text)] - [JsonSubtypes.KnownSubType(typeof(RichTextEquation), RichTextType.Equation)] - [JsonSubtypes.KnownSubType(typeof(RichTextMention), RichTextType.Mention)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RichTextText), RichTextType.Text)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RichTextEquation), RichTextType.Equation)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RichTextMention), RichTextType.Mention)] public class RichTextBase { [JsonProperty("plain_text")] @@ -42,7 +42,7 @@ public class Annotations public bool IsCode { get; set; } [JsonProperty("color")] - // color: Color | BackgroundColor - public string Color { get; set; } + [JsonConverter(typeof(StringEnumConverter))] + public Color? Color { get; set; } } } diff --git a/Src/Notion.Client/Models/Database/RichText/RichTextMention.cs b/Src/Notion.Client/Models/Database/RichText/RichTextMention.cs index 02f6beb0..5a5b6d0c 100644 --- a/Src/Notion.Client/Models/Database/RichText/RichTextMention.cs +++ b/Src/Notion.Client/Models/Database/RichText/RichTextMention.cs @@ -25,7 +25,7 @@ public class Mention public ObjectId Database { get; set; } [JsonProperty("date")] - public DatePropertyValue Date { get; set; } + public Date Date { get; set; } } public class ObjectId diff --git a/Src/Notion.Client/Models/Database/RichText/RichTextType.cs b/Src/Notion.Client/Models/Database/RichText/RichTextType.cs index 59bc414f..91031138 100644 --- a/Src/Notion.Client/Models/Database/RichText/RichTextType.cs +++ b/Src/Notion.Client/Models/Database/RichText/RichTextType.cs @@ -1,7 +1,9 @@ -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMember.Global")] public enum RichTextType { [EnumMember(Value = null)] diff --git a/Src/Notion.Client/Models/EmojiObject.cs b/Src/Notion.Client/Models/EmojiObject.cs index 95c67b61..9b5c64df 100644 --- a/Src/Notion.Client/Models/EmojiObject.cs +++ b/Src/Notion.Client/Models/EmojiObject.cs @@ -4,10 +4,10 @@ namespace Notion.Client { public class EmojiObject : IPageIcon { - [JsonProperty("type")] - public string Type { get; set; } - [JsonProperty("emoji")] public string Emoji { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } } } diff --git a/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs b/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs index 9c258ceb..6e2627cf 100644 --- a/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs +++ b/Src/Notion.Client/Models/File/FIleInput/ExternalFileInput.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; namespace Notion.Client { @@ -7,6 +8,12 @@ public class ExternalFileInput : IFileObjectInput [JsonProperty("external")] public Data External { get; set; } + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } + public class Data { [JsonProperty("url")] diff --git a/Src/Notion.Client/Models/File/FIleInput/IFileObjectInput.cs b/Src/Notion.Client/Models/File/FIleInput/IFileObjectInput.cs index 31df1573..5883b7c1 100644 --- a/Src/Notion.Client/Models/File/FIleInput/IFileObjectInput.cs +++ b/Src/Notion.Client/Models/File/FIleInput/IFileObjectInput.cs @@ -1,6 +1,14 @@ -namespace Notion.Client +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client { public interface IFileObjectInput { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } } } diff --git a/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs b/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs index 52d7934d..949df10c 100644 --- a/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs +++ b/Src/Notion.Client/Models/File/FIleInput/UploadedFileInput.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using Newtonsoft.Json; namespace Notion.Client @@ -8,6 +9,12 @@ public class UploadedFileInput : IFileObjectInput [JsonProperty("file")] public Data File { get; set; } + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } + public class Data { [JsonProperty("url")] diff --git a/Src/Notion.Client/Models/File/FileObject.cs b/Src/Notion.Client/Models/File/FileObject.cs index 88c701ae..0ac30a6c 100644 --- a/Src/Notion.Client/Models/File/FileObject.cs +++ b/Src/Notion.Client/Models/File/FileObject.cs @@ -1,13 +1,23 @@ -using JsonSubTypes; +using System.Collections.Generic; +using JsonSubTypes; using Newtonsoft.Json; namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(UploadedFile), "file")] - [JsonSubtypes.KnownSubType(typeof(ExternalFile), "external")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(UploadedFile), "file")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ExternalFile), "external")] public abstract class FileObject : IPageIcon { + [JsonProperty("caption")] + public IEnumerable Caption { get; set; } + + /// + /// The name of the file block, as shown in the Notion UI. Note that the UI may auto-append .pdf or other extensions. + /// + [JsonProperty("name")] + public string Name { get; set; } + [JsonProperty("type")] public virtual string Type { get; set; } } diff --git a/Src/Notion.Client/Models/File/FileObjectWithName.cs b/Src/Notion.Client/Models/File/FileObjectWithName.cs index a7381929..855a5e0f 100644 --- a/Src/Notion.Client/Models/File/FileObjectWithName.cs +++ b/Src/Notion.Client/Models/File/FileObjectWithName.cs @@ -4,8 +4,8 @@ namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(UploadedFileWithName), "file")] - [JsonSubtypes.KnownSubType(typeof(ExternalFileWithName), "external")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(UploadedFileWithName), "file")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ExternalFileWithName), "external")] public abstract class FileObjectWithName { [JsonProperty("type")] diff --git a/Src/Notion.Client/Models/Filters/Checkbox.cs b/Src/Notion.Client/Models/Filters/CheckboxFilter.cs similarity index 68% rename from Src/Notion.Client/Models/Filters/Checkbox.cs rename to Src/Notion.Client/Models/Filters/CheckboxFilter.cs index 1eb73e2e..53343e35 100644 --- a/Src/Notion.Client/Models/Filters/Checkbox.cs +++ b/Src/Notion.Client/Models/Filters/CheckboxFilter.cs @@ -1,35 +1,34 @@ -using System; -using Newtonsoft.Json; - -namespace Notion.Client -{ - public class CheckboxFilter : SinglePropertyFilter - { - [JsonProperty("checkbox")] - public Condition Checkbox { get; set; } - - public CheckboxFilter( - string propertyName, - bool? equal = null, - bool? doesNotEqual = null) - { - Property = propertyName; - Checkbox = new Condition(equal: equal, doesNotEqual: doesNotEqual); - } - - public class Condition - { - [JsonProperty("equals")] - public bool? Equal { get; set; } - - [JsonProperty("does_not_equal")] - public bool? DoesNotEqual { get; set; } - - public Condition(Nullable equal = null, Nullable doesNotEqual = null) - { - Equal = equal; - DoesNotEqual = doesNotEqual; - } - } - } +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class CheckboxFilter : SinglePropertyFilter, IRollupSubPropertyFilter + { + public CheckboxFilter( + string propertyName, + bool? equal = null, + bool? doesNotEqual = null) + { + Property = propertyName; + Checkbox = new Condition(equal, doesNotEqual); + } + + [JsonProperty("checkbox")] + public Condition Checkbox { get; set; } + + public class Condition + { + public Condition(bool? equal = null, bool? doesNotEqual = null) + { + Equal = equal; + DoesNotEqual = doesNotEqual; + } + + [JsonProperty("equals")] + public bool? Equal { get; set; } + + [JsonProperty("does_not_equal")] + public bool? DoesNotEqual { get; set; } + } + } } diff --git a/Src/Notion.Client/Models/Filters/Date.cs b/Src/Notion.Client/Models/Filters/DateFilter.cs similarity index 85% rename from Src/Notion.Client/Models/Filters/Date.cs rename to Src/Notion.Client/Models/Filters/DateFilter.cs index 44b29fb9..dde7ad47 100644 --- a/Src/Notion.Client/Models/Filters/Date.cs +++ b/Src/Notion.Client/Models/Filters/DateFilter.cs @@ -1,124 +1,125 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; - -namespace Notion.Client -{ - public class DateFilter : SinglePropertyFilter - { - [JsonProperty("date")] - public Condition Date { get; set; } - - public DateFilter( - string propertyName, - DateTime? equal = null, - DateTime? before = null, - DateTime? after = null, - DateTime? onOrBefore = null, - DateTime? onOrAfter = null, - Dictionary pastWeek = null, - Dictionary pastMonth = null, - Dictionary pastYear = null, - Dictionary nextWeek = null, - Dictionary nextMonth = null, - Dictionary nextYear = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Property = propertyName; - Date = new Condition( - equal: equal, - before: before, - after: after, - onOrBefore: onOrBefore, - onOrAfter: onOrAfter, - pastWeek: pastWeek, - pastMonth: pastMonth, - pastYear: pastYear, - nextWeek: nextWeek, - nextMonth: nextMonth, - nextYear: nextYear, - isEmpty: isEmpty, - isNotEmpty: isNotEmpty - ); - } - - public class Condition - { - [JsonProperty("equals")] - [JsonConverter(typeof(IsoDateTimeConverter))] - public DateTime? Equal { get; set; } - - [JsonProperty("before")] - [JsonConverter(typeof(IsoDateTimeConverter))] - public DateTime? Before { get; set; } - - [JsonProperty("after")] - [JsonConverter(typeof(IsoDateTimeConverter))] - public DateTime? After { get; set; } - - [JsonProperty("on_or_before")] - [JsonConverter(typeof(IsoDateTimeConverter))] - public DateTime? OnOrBefore { get; set; } - - [JsonProperty("on_or_after")] - [JsonConverter(typeof(IsoDateTimeConverter))] - public DateTime? OnOrAfter { get; set; } - - [JsonProperty("past_week")] - public Dictionary PastWeek { get; set; } - - [JsonProperty("past_month")] - public Dictionary PastMonth { get; set; } - - [JsonProperty("past_year")] - public Dictionary PastYear { get; set; } - - [JsonProperty("next_week")] - public Dictionary NextWeek { get; set; } - - [JsonProperty("next_month")] - public Dictionary NextMonth { get; set; } - - [JsonProperty("next_year")] - public Dictionary NextYear { get; set; } - - [JsonProperty("is_empty")] - public bool? IsEmpty { get; set; } - - [JsonProperty("is_not_empty")] - public bool? IsNotEmpty { get; set; } - - public Condition( - DateTime? equal = null, - DateTime? before = null, - DateTime? after = null, - DateTime? onOrBefore = null, - DateTime? onOrAfter = null, - Dictionary pastWeek = null, - Dictionary pastMonth = null, - Dictionary pastYear = null, - Dictionary nextWeek = null, - Dictionary nextMonth = null, - Dictionary nextYear = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Equal = equal; - Before = before; - After = after; - OnOrBefore = onOrBefore; - OnOrAfter = onOrAfter; - PastWeek = pastWeek; - PastMonth = pastMonth; - PastYear = pastYear; - NextWeek = nextWeek; - NextMonth = nextMonth; - NextYear = nextYear; - IsEmpty = isEmpty; - IsNotEmpty = isNotEmpty; - } - } - } +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + public class DateFilter : SinglePropertyFilter, IRollupSubPropertyFilter + { + public DateFilter( + string propertyName, + DateTime? equal = null, + DateTime? before = null, + DateTime? after = null, + DateTime? onOrBefore = null, + DateTime? onOrAfter = null, + Dictionary pastWeek = null, + Dictionary pastMonth = null, + Dictionary pastYear = null, + Dictionary nextWeek = null, + Dictionary nextMonth = null, + Dictionary nextYear = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + Date = new Condition( + equal, + before, + after, + onOrBefore, + onOrAfter, + pastWeek, + pastMonth, + pastYear, + nextWeek, + nextMonth, + nextYear, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("date")] + public Condition Date { get; set; } + + public class Condition + { + public Condition( + DateTime? equal = null, + DateTime? before = null, + DateTime? after = null, + DateTime? onOrBefore = null, + DateTime? onOrAfter = null, + Dictionary pastWeek = null, + Dictionary pastMonth = null, + Dictionary pastYear = null, + Dictionary nextWeek = null, + Dictionary nextMonth = null, + Dictionary nextYear = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Equal = equal; + Before = before; + After = after; + OnOrBefore = onOrBefore; + OnOrAfter = onOrAfter; + PastWeek = pastWeek; + PastMonth = pastMonth; + PastYear = pastYear; + NextWeek = nextWeek; + NextMonth = nextMonth; + NextYear = nextYear; + IsEmpty = isEmpty; + IsNotEmpty = isNotEmpty; + } + + [JsonProperty("equals")] + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTime? Equal { get; set; } + + [JsonProperty("before")] + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTime? Before { get; set; } + + [JsonProperty("after")] + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTime? After { get; set; } + + [JsonProperty("on_or_before")] + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTime? OnOrBefore { get; set; } + + [JsonProperty("on_or_after")] + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTime? OnOrAfter { get; set; } + + [JsonProperty("past_week")] + public Dictionary PastWeek { get; set; } + + [JsonProperty("past_month")] + public Dictionary PastMonth { get; set; } + + [JsonProperty("past_year")] + public Dictionary PastYear { get; set; } + + [JsonProperty("next_week")] + public Dictionary NextWeek { get; set; } + + [JsonProperty("next_month")] + public Dictionary NextMonth { get; set; } + + [JsonProperty("next_year")] + public Dictionary NextYear { get; set; } + + [JsonProperty("is_empty")] + public bool? IsEmpty { get; set; } + + [JsonProperty("is_not_empty")] + public bool? IsNotEmpty { get; set; } + } + } } diff --git a/Src/Notion.Client/Models/Filters/EmailFilter.cs b/Src/Notion.Client/Models/Filters/EmailFilter.cs new file mode 100644 index 00000000..36bc3dde --- /dev/null +++ b/Src/Notion.Client/Models/Filters/EmailFilter.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class EmailFilter : SinglePropertyFilter + { + public EmailFilter( + string propertyName, + string equal = null, + string doesNotEqual = null, + string contains = null, + string doesNotContain = null, + string startsWith = null, + string endsWith = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + Email = new TextFilter.Condition( + equal, + doesNotEqual, + contains, + doesNotContain, + startsWith, + endsWith, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("email")] + public TextFilter.Condition Email { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Filters/Files.cs b/Src/Notion.Client/Models/Filters/FilesFilter.cs similarity index 82% rename from Src/Notion.Client/Models/Filters/Files.cs rename to Src/Notion.Client/Models/Filters/FilesFilter.cs index e20f863d..6c4b23c6 100644 --- a/Src/Notion.Client/Models/Filters/Files.cs +++ b/Src/Notion.Client/Models/Filters/FilesFilter.cs @@ -1,37 +1,36 @@ -using Newtonsoft.Json; - -namespace Notion.Client -{ - - public class FilesFilter : SinglePropertyFilter - { - [JsonProperty("files")] - public Condition Files { get; set; } - - public FilesFilter( - string propertyName, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Property = propertyName; - Files = new Condition(isEmpty: isEmpty, isNotEmpty: isNotEmpty); - } - - public class Condition - { - [JsonProperty("is_empty")] - public bool? IsEmpty { get; set; } - - [JsonProperty("is_not_empty")] - public bool? IsNotEmpty { get; set; } - - public Condition( - bool? isEmpty = null, - bool? isNotEmpty = null) - { - IsEmpty = isEmpty; - IsNotEmpty = isNotEmpty; - } - } - } -} +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class FilesFilter : SinglePropertyFilter, IRollupSubPropertyFilter + { + public FilesFilter( + string propertyName, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + Files = new Condition(isEmpty, isNotEmpty); + } + + [JsonProperty("files")] + public Condition Files { get; set; } + + public class Condition + { + public Condition( + bool? isEmpty = null, + bool? isNotEmpty = null) + { + IsEmpty = isEmpty; + IsNotEmpty = isNotEmpty; + } + + [JsonProperty("is_empty")] + public bool? IsEmpty { get; set; } + + [JsonProperty("is_not_empty")] + public bool? IsNotEmpty { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Filters/Filter.cs b/Src/Notion.Client/Models/Filters/Filter.cs index 61f81f7e..47eced53 100644 --- a/Src/Notion.Client/Models/Filters/Filter.cs +++ b/Src/Notion.Client/Models/Filters/Filter.cs @@ -5,7 +5,6 @@ namespace Notion.Client { public class Filter { - } public class SinglePropertyFilter : Filter @@ -16,16 +15,16 @@ public class SinglePropertyFilter : Filter public class CompoundFilter : Filter { - [JsonProperty("or")] - public List Or { get; set; } - - [JsonProperty("and")] - public List And { get; set; } - public CompoundFilter(List or = null, List and = null) { Or = or; And = and; } + + [JsonProperty("or")] + public List Or { get; set; } + + [JsonProperty("and")] + public List And { get; set; } } } diff --git a/Src/Notion.Client/Models/Filters/Formula.cs b/Src/Notion.Client/Models/Filters/FormulaFilter.cs similarity index 75% rename from Src/Notion.Client/Models/Filters/Formula.cs rename to Src/Notion.Client/Models/Filters/FormulaFilter.cs index 82b58cc8..5aec5e00 100644 --- a/Src/Notion.Client/Models/Filters/Formula.cs +++ b/Src/Notion.Client/Models/Filters/FormulaFilter.cs @@ -1,53 +1,54 @@ -using Newtonsoft.Json; - -namespace Notion.Client -{ - public class FormulaFilter : SinglePropertyFilter - { - [JsonProperty("formula")] - public Condition Formula { get; set; } - - public FormulaFilter( - string propertyName, - TextFilter.Condition text = null, - CheckboxFilter.Condition checkbox = null, - NumberFilter.Condition number = null, - DateFilter.Condition date = null) - { - Property = propertyName; - Formula = new Condition( - text: text, - checkbox: checkbox, - number: number, - date: date - ); - } - - public class Condition - { - [JsonProperty("text")] - public TextFilter.Condition Text { get; set; } - - [JsonProperty("checkbox")] - public CheckboxFilter.Condition Checkbox { get; set; } - - [JsonProperty("number")] - public NumberFilter.Condition Number { get; set; } - - [JsonProperty("date")] - public DateFilter.Condition Date { get; set; } - - public Condition( - TextFilter.Condition text = null, - CheckboxFilter.Condition checkbox = null, - NumberFilter.Condition number = null, - DateFilter.Condition date = null) - { - Text = text; - Checkbox = checkbox; - Number = number; - Date = date; - } - } - } +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class FormulaFilter : SinglePropertyFilter + { + public FormulaFilter( + string propertyName, + TextFilter.Condition @string = null, + CheckboxFilter.Condition checkbox = null, + NumberFilter.Condition number = null, + DateFilter.Condition date = null) + { + Property = propertyName; + + Formula = new Condition( + @string, + checkbox, + number, + date + ); + } + + [JsonProperty("formula")] + public Condition Formula { get; set; } + + public class Condition + { + public Condition( + TextFilter.Condition @string = null, + CheckboxFilter.Condition checkbox = null, + NumberFilter.Condition number = null, + DateFilter.Condition date = null) + { + String = @string; + Checkbox = checkbox; + Number = number; + Date = date; + } + + [JsonProperty("string")] + public TextFilter.Condition String { get; set; } + + [JsonProperty("checkbox")] + public CheckboxFilter.Condition Checkbox { get; set; } + + [JsonProperty("number")] + public NumberFilter.Condition Number { get; set; } + + [JsonProperty("date")] + public DateFilter.Condition Date { get; set; } + } + } } diff --git a/Src/Notion.Client/Models/Filters/Multiselect.cs b/Src/Notion.Client/Models/Filters/MultiSelectFilter.cs similarity index 82% rename from Src/Notion.Client/Models/Filters/Multiselect.cs rename to Src/Notion.Client/Models/Filters/MultiSelectFilter.cs index bf69d110..011a127e 100644 --- a/Src/Notion.Client/Models/Filters/Multiselect.cs +++ b/Src/Notion.Client/Models/Filters/MultiSelectFilter.cs @@ -1,55 +1,54 @@ -using Newtonsoft.Json; - -namespace Notion.Client -{ - public class MultiSelectFilter : SinglePropertyFilter - { - [JsonProperty("multi_select")] - public Condition MultiSelect { get; set; } - - public MultiSelectFilter( - string propertyName, - string contains = null, - string doesNotContain = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Property = propertyName; - MultiSelect = new Condition( - contains: contains, - doesNotContain: doesNotContain, - isEmpty: isEmpty, - isNotEmpty: isNotEmpty - ); - } - - - public class Condition - { - [JsonProperty("contains")] - public string Contains { get; set; } - - [JsonProperty("does_not_contain")] - public string DoesNotContain { get; set; } - - [JsonProperty("is_empty")] - public bool? IsEmpty { get; set; } - - [JsonProperty("is_not_empty")] - public bool? IsNotEmpty { get; set; } - - public Condition( - string contains = null, - string doesNotContain = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Contains = contains; - DoesNotContain = doesNotContain; - IsEmpty = isEmpty; - IsNotEmpty = isNotEmpty; - } - } - - } -} +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class MultiSelectFilter : SinglePropertyFilter, IRollupSubPropertyFilter + { + public MultiSelectFilter( + string propertyName, + string contains = null, + string doesNotContain = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + MultiSelect = new Condition( + contains, + doesNotContain, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("multi_select")] + public Condition MultiSelect { get; set; } + + public class Condition + { + public Condition( + string contains = null, + string doesNotContain = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Contains = contains; + DoesNotContain = doesNotContain; + IsEmpty = isEmpty; + IsNotEmpty = isNotEmpty; + } + + [JsonProperty("contains")] + public string Contains { get; set; } + + [JsonProperty("does_not_contain")] + public string DoesNotContain { get; set; } + + [JsonProperty("is_empty")] + public bool? IsEmpty { get; set; } + + [JsonProperty("is_not_empty")] + public bool? IsNotEmpty { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Filters/Number.cs b/Src/Notion.Client/Models/Filters/NumberFilter.cs similarity index 82% rename from Src/Notion.Client/Models/Filters/Number.cs rename to Src/Notion.Client/Models/Filters/NumberFilter.cs index 9282226a..1a2f6879 100644 --- a/Src/Notion.Client/Models/Filters/Number.cs +++ b/Src/Notion.Client/Models/Filters/NumberFilter.cs @@ -1,82 +1,82 @@ -using Newtonsoft.Json; - -namespace Notion.Client -{ - public class NumberFilter : SinglePropertyFilter - { - [JsonProperty("number")] - public Condition Number { get; set; } - - public NumberFilter( - string propertyName, - double? equal = null, - double? doesNotEqual = null, - double? greaterThan = null, - double? lessThan = null, - double? greaterThanOrEqualTo = null, - double? lessThanOrEqualTo = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Property = propertyName; - Number = new Condition( - equal: equal, - doesNotEqual: doesNotEqual, - greaterThan: greaterThan, - lessThan: lessThan, - greaterThanOrEqualTo: greaterThanOrEqualTo, - lessThanOrEqualTo: lessThanOrEqualTo, - isEmpty: isEmpty, - isNotEmpty: isNotEmpty - ); - } - - public class Condition - { - [JsonProperty("equals")] - public double? Equal { get; set; } - - [JsonProperty("does_not_equal")] - public double? DoesNotEqual { get; set; } - - [JsonProperty("greater_than")] - public double? GreaterThan { get; set; } - - [JsonProperty("less_than")] - public double? LessThan { get; set; } - - [JsonProperty("greater_than_or_equal_to")] - public double? GreaterThanOrEqualTo { get; set; } - - [JsonProperty("less_than_or_equal_to")] - public double? LessThanOrEqualTo { get; set; } - - [JsonProperty("is_empty")] - public bool? IsEmpty { get; set; } - - [JsonProperty("is_not_empty")] - public bool? IsNotEmpty { get; set; } - - public Condition( - double? equal = null, - double? doesNotEqual = null, - double? greaterThan = null, - double? lessThan = null, - double? greaterThanOrEqualTo = null, - double? lessThanOrEqualTo = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Equal = equal; - DoesNotEqual = doesNotEqual; - GreaterThan = greaterThan; - LessThan = lessThan; - GreaterThanOrEqualTo = greaterThanOrEqualTo; - LessThanOrEqualTo = lessThanOrEqualTo; - IsEmpty = isEmpty; - IsNotEmpty = isNotEmpty; - } - } - - } -} +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class NumberFilter : SinglePropertyFilter, IRollupSubPropertyFilter + { + public NumberFilter( + string propertyName, + double? equal = null, + double? doesNotEqual = null, + double? greaterThan = null, + double? lessThan = null, + double? greaterThanOrEqualTo = null, + double? lessThanOrEqualTo = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + Number = new Condition( + equal, + doesNotEqual, + greaterThan, + lessThan, + greaterThanOrEqualTo, + lessThanOrEqualTo, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("number")] + public Condition Number { get; set; } + + public class Condition + { + public Condition( + double? equal = null, + double? doesNotEqual = null, + double? greaterThan = null, + double? lessThan = null, + double? greaterThanOrEqualTo = null, + double? lessThanOrEqualTo = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Equal = equal; + DoesNotEqual = doesNotEqual; + GreaterThan = greaterThan; + LessThan = lessThan; + GreaterThanOrEqualTo = greaterThanOrEqualTo; + LessThanOrEqualTo = lessThanOrEqualTo; + IsEmpty = isEmpty; + IsNotEmpty = isNotEmpty; + } + + [JsonProperty("equals")] + public double? Equal { get; set; } + + [JsonProperty("does_not_equal")] + public double? DoesNotEqual { get; set; } + + [JsonProperty("greater_than")] + public double? GreaterThan { get; set; } + + [JsonProperty("less_than")] + public double? LessThan { get; set; } + + [JsonProperty("greater_than_or_equal_to")] + public double? GreaterThanOrEqualTo { get; set; } + + [JsonProperty("less_than_or_equal_to")] + public double? LessThanOrEqualTo { get; set; } + + [JsonProperty("is_empty")] + public bool? IsEmpty { get; set; } + + [JsonProperty("is_not_empty")] + public bool? IsNotEmpty { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Filters/People.cs b/Src/Notion.Client/Models/Filters/PeopleFilter.cs similarity index 82% rename from Src/Notion.Client/Models/Filters/People.cs rename to Src/Notion.Client/Models/Filters/PeopleFilter.cs index 6138e3d2..6b83dda4 100644 --- a/Src/Notion.Client/Models/Filters/People.cs +++ b/Src/Notion.Client/Models/Filters/PeopleFilter.cs @@ -1,53 +1,54 @@ -using Newtonsoft.Json; - -namespace Notion.Client -{ - public class PeopleFilter : SinglePropertyFilter - { - [JsonProperty("people")] - public Condition People { get; set; } - - public PeopleFilter( - string propertyName, - string contains = null, - string doesNotContain = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Property = propertyName; - People = new Condition( - contains: contains, - doesNotContain: doesNotContain, - isEmpty: isEmpty, - isNotEmpty: isNotEmpty - ); - } - - public class Condition - { - [JsonProperty("contains")] - public string Contains { get; set; } - - [JsonProperty("does_not_contain")] - public string DoesNotContain { get; set; } - - [JsonProperty("is_empty")] - public bool? IsEmpty { get; set; } - - [JsonProperty("is_not_empty")] - public bool? IsNotEmpty { get; set; } - - public Condition( - string contains = null, - string doesNotContain = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Contains = contains; - DoesNotContain = doesNotContain; - IsEmpty = isEmpty; - IsNotEmpty = isNotEmpty; - } - } - } -} +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class PeopleFilter : SinglePropertyFilter, IRollupSubPropertyFilter + { + public PeopleFilter( + string propertyName, + string contains = null, + string doesNotContain = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + People = new Condition( + contains, + doesNotContain, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("people")] + public Condition People { get; set; } + + public class Condition + { + public Condition( + string contains = null, + string doesNotContain = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Contains = contains; + DoesNotContain = doesNotContain; + IsEmpty = isEmpty; + IsNotEmpty = isNotEmpty; + } + + [JsonProperty("contains")] + public string Contains { get; set; } + + [JsonProperty("does_not_contain")] + public string DoesNotContain { get; set; } + + [JsonProperty("is_empty")] + public bool? IsEmpty { get; set; } + + [JsonProperty("is_not_empty")] + public bool? IsNotEmpty { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Filters/PhoneNumberFilter.cs b/Src/Notion.Client/Models/Filters/PhoneNumberFilter.cs new file mode 100644 index 00000000..9ea9cf68 --- /dev/null +++ b/Src/Notion.Client/Models/Filters/PhoneNumberFilter.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class PhoneNumberFilter : SinglePropertyFilter + { + public PhoneNumberFilter( + string propertyName, + string equal = null, + string doesNotEqual = null, + string contains = null, + string doesNotContain = null, + string startsWith = null, + string endsWith = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + Text = new TextFilter.Condition( + equal, + doesNotEqual, + contains, + doesNotContain, + startsWith, + endsWith, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("phone_number")] + public TextFilter.Condition Text { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Filters/Relation.cs b/Src/Notion.Client/Models/Filters/RelationFilter.cs similarity index 82% rename from Src/Notion.Client/Models/Filters/Relation.cs rename to Src/Notion.Client/Models/Filters/RelationFilter.cs index 26f7214a..bff5f498 100644 --- a/Src/Notion.Client/Models/Filters/Relation.cs +++ b/Src/Notion.Client/Models/Filters/RelationFilter.cs @@ -1,53 +1,54 @@ -using Newtonsoft.Json; - -namespace Notion.Client -{ - public class RelationFilter : SinglePropertyFilter - { - [JsonProperty("relation")] - public Condition Relation { get; set; } - - public RelationFilter( - string propertyName, - string contains = null, - string doesNotContain = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Property = propertyName; - Relation = new Condition( - contains: contains, - doesNotContain: doesNotContain, - isEmpty: isEmpty, - isNotEmpty: isNotEmpty - ); - } - - public class Condition - { - [JsonProperty("contains")] - public string Contains { get; set; } - - [JsonProperty("does_not_contain")] - public string DoesNotContain { get; set; } - - [JsonProperty("is_empty")] - public bool? IsEmpty { get; set; } - - [JsonProperty("is_not_empty")] - public bool? IsNotEmpty { get; set; } - - public Condition( - string contains = null, - string doesNotContain = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Contains = contains; - DoesNotContain = doesNotContain; - IsEmpty = isEmpty; - IsNotEmpty = isNotEmpty; - } - } - } -} +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class RelationFilter : SinglePropertyFilter, IRollupSubPropertyFilter + { + public RelationFilter( + string propertyName, + string contains = null, + string doesNotContain = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + Relation = new Condition( + contains, + doesNotContain, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("relation")] + public Condition Relation { get; set; } + + public class Condition + { + public Condition( + string contains = null, + string doesNotContain = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Contains = contains; + DoesNotContain = doesNotContain; + IsEmpty = isEmpty; + IsNotEmpty = isNotEmpty; + } + + [JsonProperty("contains")] + public string Contains { get; set; } + + [JsonProperty("does_not_contain")] + public string DoesNotContain { get; set; } + + [JsonProperty("is_empty")] + public bool? IsEmpty { get; set; } + + [JsonProperty("is_not_empty")] + public bool? IsNotEmpty { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Filters/Text.cs b/Src/Notion.Client/Models/Filters/RichTextFilter.cs similarity index 77% rename from Src/Notion.Client/Models/Filters/Text.cs rename to Src/Notion.Client/Models/Filters/RichTextFilter.cs index 3446e854..733b93ba 100644 --- a/Src/Notion.Client/Models/Filters/Text.cs +++ b/Src/Notion.Client/Models/Filters/RichTextFilter.cs @@ -1,81 +1,85 @@ -using Newtonsoft.Json; - -namespace Notion.Client -{ - public class TextFilter : SinglePropertyFilter - { - [JsonProperty("text")] - public Condition Text { get; set; } - - public TextFilter( - string propertyName, - string equal = null, - string doesNotEqual = null, - string contains = null, - string doesNotContain = null, - string startsWith = null, - string endsWith = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Property = propertyName; - Text = new Condition( - equal: equal, - doesNotEqual: doesNotEqual, - contains: contains, - doesNotContain: doesNotContain, - startsWith: startsWith, - endsWith: endsWith, - isEmpty: isEmpty, - isNotEmpty: isNotEmpty - ); - } - - public class Condition - { - [JsonProperty("equals")] - public string Equal { get; set; } - - [JsonProperty("does_not_equal")] - public string DoesNotEqual { get; set; } - - [JsonProperty("contains")] - public string Contains { get; set; } - - [JsonProperty("does_not_contain")] - public string DoesNotContain { get; set; } - - [JsonProperty("starts_with")] - public string StartsWith { get; set; } - - [JsonProperty("ends_with")] - public string EndsWith { get; set; } - - [JsonProperty("is_empty")] - public bool? IsEmpty { get; set; } - - [JsonProperty("is_not_empty")] - public bool? IsNotEmpty { get; set; } - - public Condition( - string equal = null, - string doesNotEqual = null, - string contains = null, - string doesNotContain = null, - string startsWith = null, - string endsWith = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Equal = equal; - DoesNotEqual = doesNotEqual; - Contains = contains; - DoesNotContain = doesNotContain; - StartsWith = startsWith; - EndsWith = endsWith; - IsEmpty = isEmpty; - IsNotEmpty = isNotEmpty; - } - } - } -} +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class RichTextFilter : SinglePropertyFilter, IRollupSubPropertyFilter + { + public RichTextFilter( + string propertyName, + string equal = null, + string doesNotEqual = null, + string contains = null, + string doesNotContain = null, + string startsWith = null, + string endsWith = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + RichText = new TextFilter.Condition( + equal, + doesNotEqual, + contains, + doesNotContain, + startsWith, + endsWith, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("rich_text")] + public TextFilter.Condition RichText { get; set; } + } + + public static class TextFilter + { + public class Condition + { + public Condition( + string equal = null, + string doesNotEqual = null, + string contains = null, + string doesNotContain = null, + string startsWith = null, + string endsWith = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Equal = equal; + DoesNotEqual = doesNotEqual; + Contains = contains; + DoesNotContain = doesNotContain; + StartsWith = startsWith; + EndsWith = endsWith; + IsEmpty = isEmpty; + IsNotEmpty = isNotEmpty; + } + + [JsonProperty("equals")] + public string Equal { get; set; } + + [JsonProperty("does_not_equal")] + public string DoesNotEqual { get; set; } + + [JsonProperty("contains")] + public string Contains { get; set; } + + [JsonProperty("does_not_contain")] + public string DoesNotContain { get; set; } + + [JsonProperty("starts_with")] + public string StartsWith { get; set; } + + [JsonProperty("ends_with")] + public string EndsWith { get; set; } + + [JsonProperty("is_empty")] + public bool? IsEmpty { get; set; } + + [JsonProperty("is_not_empty")] + public bool? IsNotEmpty { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Filters/Rollup/IRollupSubPropertyFilter.cs b/Src/Notion.Client/Models/Filters/Rollup/IRollupSubPropertyFilter.cs new file mode 100644 index 00000000..b9c850c5 --- /dev/null +++ b/Src/Notion.Client/Models/Filters/Rollup/IRollupSubPropertyFilter.cs @@ -0,0 +1,6 @@ +namespace Notion.Client +{ + public interface IRollupSubPropertyFilter + { + } +} diff --git a/Src/Notion.Client/Models/Filters/Rollup/RollupFilter.cs b/Src/Notion.Client/Models/Filters/Rollup/RollupFilter.cs new file mode 100644 index 00000000..df641729 --- /dev/null +++ b/Src/Notion.Client/Models/Filters/Rollup/RollupFilter.cs @@ -0,0 +1,61 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class RollupFilter : SinglePropertyFilter + { + public RollupFilter( + string propertyName, + IRollupSubPropertyFilter any = null, + IRollupSubPropertyFilter none = null, + IRollupSubPropertyFilter every = null, + DateFilter.Condition date = null, + NumberFilter.Condition number = null) + { + Property = propertyName; + + Rollup = new Condition( + any, + none, + every, + date, + number + ); + } + + [JsonProperty("rollup")] + public Condition Rollup { get; set; } + + public class Condition + { + public Condition( + IRollupSubPropertyFilter any = null, + IRollupSubPropertyFilter none = null, + IRollupSubPropertyFilter every = null, + DateFilter.Condition date = null, + NumberFilter.Condition number = null) + { + Any = any; + None = none; + Every = every; + Date = date; + Number = number; + } + + [JsonProperty("any")] + public IRollupSubPropertyFilter Any { get; set; } + + [JsonProperty("none")] + public IRollupSubPropertyFilter None { get; set; } + + [JsonProperty("every")] + public IRollupSubPropertyFilter Every { get; set; } + + [JsonProperty("date")] + public DateFilter.Condition Date { get; set; } + + [JsonProperty("number")] + public NumberFilter.Condition Number { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Filters/Select.cs b/Src/Notion.Client/Models/Filters/SelectFilter.cs similarity index 83% rename from Src/Notion.Client/Models/Filters/Select.cs rename to Src/Notion.Client/Models/Filters/SelectFilter.cs index bf246be2..064334f1 100644 --- a/Src/Notion.Client/Models/Filters/Select.cs +++ b/Src/Notion.Client/Models/Filters/SelectFilter.cs @@ -1,53 +1,54 @@ -using Newtonsoft.Json; - -namespace Notion.Client -{ - public class SelectFilter : SinglePropertyFilter - { - [JsonProperty("select")] - public Condition Select { get; set; } - - public SelectFilter( - string propertyName, - string equal = null, - string doesNotEqual = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Property = propertyName; - Select = new Condition( - equal: equal, - doesNotEqual: doesNotEqual, - isEmpty: isEmpty, - isNotEmpty: isNotEmpty - ); - } - - public class Condition - { - [JsonProperty("equals")] - public string Equal { get; set; } - - [JsonProperty("does_not_equal")] - public string DoesNotEqual { get; set; } - - [JsonProperty("is_empty")] - public bool? IsEmpty { get; set; } - - [JsonProperty("is_not_empty")] - public bool? IsNotEmpty { get; set; } - - public Condition( - string equal = null, - string doesNotEqual = null, - bool? isEmpty = null, - bool? isNotEmpty = null) - { - Equal = equal; - DoesNotEqual = doesNotEqual; - IsEmpty = isEmpty; - IsNotEmpty = isNotEmpty; - } - } - } -} +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class SelectFilter : SinglePropertyFilter, IRollupSubPropertyFilter + { + public SelectFilter( + string propertyName, + string equal = null, + string doesNotEqual = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + Select = new Condition( + equal, + doesNotEqual, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("select")] + public Condition Select { get; set; } + + public class Condition + { + public Condition( + string equal = null, + string doesNotEqual = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Equal = equal; + DoesNotEqual = doesNotEqual; + IsEmpty = isEmpty; + IsNotEmpty = isNotEmpty; + } + + [JsonProperty("equals")] + public string Equal { get; set; } + + [JsonProperty("does_not_equal")] + public string DoesNotEqual { get; set; } + + [JsonProperty("is_empty")] + public bool? IsEmpty { get; set; } + + [JsonProperty("is_not_empty")] + public bool? IsNotEmpty { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Filters/StatusFilter.cs b/Src/Notion.Client/Models/Filters/StatusFilter.cs new file mode 100644 index 00000000..0af0a22c --- /dev/null +++ b/Src/Notion.Client/Models/Filters/StatusFilter.cs @@ -0,0 +1,54 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class StatusFilter : SinglePropertyFilter, IRollupSubPropertyFilter + { + public StatusFilter( + string propertyName, + string equal = null, + string doesNotEqual = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + Status = new Condition( + equal, + doesNotEqual, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("status")] + public Condition Status { get; set; } + + public class Condition + { + public Condition( + string equal = null, + string doesNotEqual = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Equal = equal; + DoesNotEqual = doesNotEqual; + IsEmpty = isEmpty; + IsNotEmpty = isNotEmpty; + } + + [JsonProperty("equals")] + public string Equal { get; set; } + + [JsonProperty("does_not_equal")] + public string DoesNotEqual { get; set; } + + [JsonProperty("is_empty")] + public bool? IsEmpty { get; set; } + + [JsonProperty("is_not_empty")] + public bool? IsNotEmpty { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/Filters/TimestampCreatedTimeFilter.cs b/Src/Notion.Client/Models/Filters/TimestampCreatedTimeFilter.cs new file mode 100644 index 00000000..80f9dcf4 --- /dev/null +++ b/Src/Notion.Client/Models/Filters/TimestampCreatedTimeFilter.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TimestampCreatedTimeFilter : Filter + { + public TimestampCreatedTimeFilter( + DateTime? equal = null, + DateTime? before = null, + DateTime? after = null, + DateTime? onOrBefore = null, + DateTime? onOrAfter = null, + Dictionary pastWeek = null, + Dictionary pastMonth = null, + Dictionary pastYear = null, + Dictionary nextWeek = null, + Dictionary nextMonth = null, + Dictionary nextYear = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + CreatedTime = new DateFilter.Condition( + equal, + before, + after, + onOrBefore, + onOrAfter, + pastWeek, + pastMonth, + pastYear, + nextWeek, + nextMonth, + nextYear, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("timestamp")] + public string Timestamp => "created_time"; + + [JsonProperty("created_time")] + public DateFilter.Condition CreatedTime { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Filters/TimestampLastEditedTimeFilter.cs b/Src/Notion.Client/Models/Filters/TimestampLastEditedTimeFilter.cs new file mode 100644 index 00000000..1cd22974 --- /dev/null +++ b/Src/Notion.Client/Models/Filters/TimestampLastEditedTimeFilter.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TimestampLastEditedTimeFilter : Filter + { + public TimestampLastEditedTimeFilter( + DateTime? equal = null, + DateTime? before = null, + DateTime? after = null, + DateTime? onOrBefore = null, + DateTime? onOrAfter = null, + Dictionary pastWeek = null, + Dictionary pastMonth = null, + Dictionary pastYear = null, + Dictionary nextWeek = null, + Dictionary nextMonth = null, + Dictionary nextYear = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + LastEditedTime = new DateFilter.Condition( + equal, + before, + after, + onOrBefore, + onOrAfter, + pastWeek, + pastMonth, + pastYear, + nextWeek, + nextMonth, + nextYear, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("timestamp")] + public string Timestamp => "last_edited_time"; + + [JsonProperty("last_edited_time")] + public DateFilter.Condition LastEditedTime { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Filters/TitleFilter.cs b/Src/Notion.Client/Models/Filters/TitleFilter.cs new file mode 100644 index 00000000..1e0d4303 --- /dev/null +++ b/Src/Notion.Client/Models/Filters/TitleFilter.cs @@ -0,0 +1,35 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class TitleFilter : SinglePropertyFilter + { + public TitleFilter( + string propertyName, + string equal = null, + string doesNotEqual = null, + string contains = null, + string doesNotContain = null, + string startsWith = null, + string endsWith = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + Title = new TextFilter.Condition( + equal, + doesNotEqual, + contains, + doesNotContain, + startsWith, + endsWith, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("title")] + public TextFilter.Condition Title { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Filters/URLFilter.cs b/Src/Notion.Client/Models/Filters/URLFilter.cs new file mode 100644 index 00000000..7c722832 --- /dev/null +++ b/Src/Notion.Client/Models/Filters/URLFilter.cs @@ -0,0 +1,37 @@ +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; + +namespace Notion.Client +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + public class URLFilter : SinglePropertyFilter + { + public URLFilter( + string propertyName, + string equal = null, + string doesNotEqual = null, + string contains = null, + string doesNotContain = null, + string startsWith = null, + string endsWith = null, + bool? isEmpty = null, + bool? isNotEmpty = null) + { + Property = propertyName; + + URL = new TextFilter.Condition( + equal, + doesNotEqual, + contains, + doesNotContain, + startsWith, + endsWith, + isEmpty, + isNotEmpty + ); + } + + [JsonProperty("url")] + public TextFilter.Condition URL { get; set; } + } +} diff --git a/Src/Notion.Client/Models/IObject.cs b/Src/Notion.Client/Models/IObject.cs index 2f72f929..b8ab1108 100644 --- a/Src/Notion.Client/Models/IObject.cs +++ b/Src/Notion.Client/Models/IObject.cs @@ -5,10 +5,10 @@ namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "object")] - [JsonSubtypes.KnownSubType(typeof(Page), ObjectType.Page)] - [JsonSubtypes.KnownSubType(typeof(Database), ObjectType.Database)] - [JsonSubtypes.KnownSubType(typeof(IBlock), ObjectType.Block)] - [JsonSubtypes.KnownSubType(typeof(User), ObjectType.User)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(Page), ObjectType.Page)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(Database), ObjectType.Database)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(IBlock), ObjectType.Block)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(User), ObjectType.User)] public interface IObject { [JsonProperty("id")] diff --git a/Src/Notion.Client/Models/ObjectType.cs b/Src/Notion.Client/Models/ObjectType.cs index d493254c..4a9dc77d 100644 --- a/Src/Notion.Client/Models/ObjectType.cs +++ b/Src/Notion.Client/Models/ObjectType.cs @@ -15,5 +15,8 @@ public enum ObjectType [EnumMember(Value = "user")] User, + + [EnumMember(Value = "comment")] + Comment } } diff --git a/Src/Notion.Client/Models/Page/IPageIcon.cs b/Src/Notion.Client/Models/Page/IPageIcon.cs index a89184fe..5d692185 100644 --- a/Src/Notion.Client/Models/Page/IPageIcon.cs +++ b/Src/Notion.Client/Models/Page/IPageIcon.cs @@ -4,9 +4,10 @@ namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(EmojiObject), "emoji")] - [JsonSubtypes.KnownSubType(typeof(FileObject), "file")] - [JsonSubtypes.KnownSubType(typeof(FileObject), "external")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(EmojiObject), "emoji")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CustomEmojiObject), "custom_emoji")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(FileObject), "file")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(FileObject), "external")] public interface IPageIcon { [JsonProperty("type")] diff --git a/Src/Notion.Client/Models/Page/IPageParent.cs b/Src/Notion.Client/Models/Page/IPageParent.cs index 8301d0d5..b9111d91 100644 --- a/Src/Notion.Client/Models/Page/IPageParent.cs +++ b/Src/Notion.Client/Models/Page/IPageParent.cs @@ -4,9 +4,10 @@ namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(DatabaseParent), ParentType.DatabaseId)] - [JsonSubtypes.KnownSubType(typeof(PageParent), ParentType.PageId)] - [JsonSubtypes.KnownSubType(typeof(WorkspaceParent), ParentType.Workspace)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(DatabaseParent), ParentType.DatabaseId)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(PageParent), ParentType.PageId)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(WorkspaceParent), ParentType.Workspace)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(BlockParent), ParentType.BlockId)] public interface IPageParent : IParent { } diff --git a/Src/Notion.Client/Models/Page/Page.cs b/Src/Notion.Client/Models/Page/Page.cs index 352f9d13..fe4eb0de 100644 --- a/Src/Notion.Client/Models/Page/Page.cs +++ b/Src/Notion.Client/Models/Page/Page.cs @@ -4,64 +4,74 @@ namespace Notion.Client { - public class Page : IObject + public class Page : IObject, IObjectModificationData, IWikiDatabase { /// - /// Object type + /// The parent of this page. Can be a database, page, or workspace. /// - public ObjectType Object => ObjectType.Page; + [JsonProperty("parent")] + public IPageParent Parent { get; set; } /// - /// Unique identifier of the page. + /// Indicates whether the page is currently in the trash. /// - public string Id { get; set; } + [JsonProperty("in_trash")] + public bool InTrash { get; set; } /// - /// The parent of this page. Can be a database, page, or workspace. + /// Property values of this page. /// - [JsonProperty("parent")] - public IPageParent Parent { get; set; } + [JsonProperty("properties")] + public IDictionary Properties { get; set; } /// - /// Date and time when this page was created. + /// The URL of the Notion page. /// - [JsonProperty("created_time")] - public DateTime CreatedTime { get; set; } + [JsonProperty("url")] + public string Url { get; set; } /// - /// Date and time when this page was updated. + /// Page icon. /// - [JsonProperty("last_edited_time")] - public DateTime LastEditedTime { get; set; } + [JsonProperty("icon")] + public IPageIcon Icon { get; set; } /// - /// The archived status of the page. + /// Page cover image. /// - [JsonProperty("archived")] - public bool IsArchived { get; set; } + [JsonProperty("cover")] + public FileObject Cover { get; set; } /// - /// Property values of this page. + /// Object type /// - [JsonProperty("properties")] - public IDictionary Properties { get; set; } + public ObjectType Object => ObjectType.Page; /// - /// The URL of the Notion page. + /// Unique identifier of the page. /// - [JsonProperty("url")] - public string Url { get; set; } + public string Id { get; set; } /// - /// Page icon. + /// Date and time when this page was created. /// - [JsonProperty("icon")] - public IPageIcon Icon { get; set; } + [JsonProperty("created_time")] + public DateTime CreatedTime { get; set; } /// - /// Page cover image. + /// Date and time when this page was updated. /// - [JsonProperty("cover")] - public FileObject Cover { get; set; } + [JsonProperty("last_edited_time")] + public DateTime LastEditedTime { get; set; } + + public PartialUser CreatedBy { get; set; } + + public PartialUser LastEditedBy { get; set; } + + /// + /// The public page URL if the page has been published to the web. Otherwise, null. + /// + [JsonProperty("public_url")] + public string PublicUrl { get; set; } } } diff --git a/Src/Notion.Client/Models/Page/PagePropertyOnId.cs b/Src/Notion.Client/Models/Page/PagePropertyOnId.cs new file mode 100644 index 00000000..00005986 --- /dev/null +++ b/Src/Notion.Client/Models/Page/PagePropertyOnId.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class PagePropertyOnId + { + [JsonProperty("id")] + public string Id { get; set; } + } +} diff --git a/Src/Notion.Client/Models/PaginatedList.cs b/Src/Notion.Client/Models/PaginatedList.cs index e126125a..d0b1107f 100644 --- a/Src/Notion.Client/Models/PaginatedList.cs +++ b/Src/Notion.Client/Models/PaginatedList.cs @@ -25,5 +25,8 @@ public class PaginatedList [JsonProperty("next_cursor")] public string NextCursor { get; set; } + + [JsonProperty("type")] + public string Type { get; set; } } } diff --git a/Src/Notion.Client/Models/Parents/BlockParent.cs b/Src/Notion.Client/Models/Parents/BlockParent.cs new file mode 100644 index 00000000..e3fd7749 --- /dev/null +++ b/Src/Notion.Client/Models/Parents/BlockParent.cs @@ -0,0 +1,18 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class BlockParent : IPageParent, IDatabaseParent, IBlockParent, ICommentParent + { + /// + /// The ID of the block that the element belongs to. + /// + [JsonProperty("block_id")] + public string BlockId { get; set; } + + /// + /// Always has a value "block_id" + /// + public ParentType Type { get; set; } + } +} diff --git a/Src/Notion.Client/Models/Parents/DatabaseParent.cs b/Src/Notion.Client/Models/Parents/DatabaseParent.cs index c3e028e7..878dbe89 100644 --- a/Src/Notion.Client/Models/Parents/DatabaseParent.cs +++ b/Src/Notion.Client/Models/Parents/DatabaseParent.cs @@ -2,17 +2,17 @@ namespace Notion.Client { - public class DatabaseParent : IPageParent + public class DatabaseParent : IPageParent, IBlockParent { /// - /// Always "database_id" + /// The ID of the database that this page belongs to. /// - public ParentType Type { get; set; } + [JsonProperty("database_id")] + public string DatabaseId { get; set; } /// - /// The ID of the database that this page belongs to. + /// Always "database_id" /// - [JsonProperty("database_id")] - public string DatabaseId { get; set; } + public ParentType Type { get; set; } } } diff --git a/Src/Notion.Client/Models/Parents/IParent.cs b/Src/Notion.Client/Models/Parents/IParent.cs index e2c50e5f..ccf3999b 100644 --- a/Src/Notion.Client/Models/Parents/IParent.cs +++ b/Src/Notion.Client/Models/Parents/IParent.cs @@ -1,8 +1,10 @@ -using Newtonsoft.Json; +using System.Diagnostics.CodeAnalysis; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMemberInSuper.Global")] public interface IParent { [JsonConverter(typeof(StringEnumConverter))] diff --git a/Src/Notion.Client/Models/Parents/PageParent.cs b/Src/Notion.Client/Models/Parents/PageParent.cs index ff185747..565f7ccc 100644 --- a/Src/Notion.Client/Models/Parents/PageParent.cs +++ b/Src/Notion.Client/Models/Parents/PageParent.cs @@ -2,17 +2,17 @@ namespace Notion.Client { - public class PageParent : IPageParent, IDatabaseParent + public class PageParent : IPageParent, IDatabaseParent, IBlockParent, ICommentParent { /// - /// Always "page_id". + /// The ID of the page that this page belongs to. /// - public ParentType Type { get; set; } + [JsonProperty("page_id")] + public string PageId { get; set; } /// - /// The ID of the page that this page belongs to. + /// Always "page_id". /// - [JsonProperty("page_id")] - public string PageId { get; set; } + public ParentType Type { get; set; } } } diff --git a/Src/Notion.Client/Models/Parents/ParentType.cs b/Src/Notion.Client/Models/Parents/ParentType.cs index 488f81b0..347414f9 100644 --- a/Src/Notion.Client/Models/Parents/ParentType.cs +++ b/Src/Notion.Client/Models/Parents/ParentType.cs @@ -1,7 +1,9 @@ -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMember.Global")] public enum ParentType { [EnumMember(Value = null)] @@ -14,6 +16,9 @@ public enum ParentType PageId, [EnumMember(Value = "workspace")] - Workspace + Workspace, + + [EnumMember(Value = "block_id")] + BlockId } } diff --git a/Src/Notion.Client/Models/Parents/WorkspaceParent.cs b/Src/Notion.Client/Models/Parents/WorkspaceParent.cs index fc645b60..a0b5e1c4 100644 --- a/Src/Notion.Client/Models/Parents/WorkspaceParent.cs +++ b/Src/Notion.Client/Models/Parents/WorkspaceParent.cs @@ -1,9 +1,9 @@ namespace Notion.Client { - public class WorkspaceParent : IPageParent, IDatabaseParent + public class WorkspaceParent : IPageParent, IDatabaseParent, IBlockParent { /// - /// Always "workspace". + /// Always "workspace". /// public ParentType Type { get; set; } } diff --git a/Src/Notion.Client/Models/PropertyItems/IPropertyItemObject.cs b/Src/Notion.Client/Models/PropertyItems/IPropertyItemObject.cs index e6eb91c2..58db1a7d 100644 --- a/Src/Notion.Client/Models/PropertyItems/IPropertyItemObject.cs +++ b/Src/Notion.Client/Models/PropertyItems/IPropertyItemObject.cs @@ -4,8 +4,8 @@ namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "object")] - [JsonSubtypes.KnownSubType(typeof(SimplePropertyItem), "property_item")] - [JsonSubtypes.KnownSubType(typeof(ListPropertyItem), "list")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(SimplePropertyItem), "property_item")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ListPropertyItem), "list")] public interface IPropertyItemObject { [JsonProperty("object")] @@ -13,5 +13,15 @@ public interface IPropertyItemObject [JsonProperty("type")] string Type { get; } + + [JsonProperty("id")] + string Id { get; } + + /// + /// Only present in paginated property values with another page of results. If present, the url the user can request to + /// get the next page of results. + /// + [JsonProperty("next_url")] + string NextURL { get; } } } diff --git a/Src/Notion.Client/Models/PropertyItems/ListPropertyItem.cs b/Src/Notion.Client/Models/PropertyItems/ListPropertyItem.cs index 3ca83d81..d337ccfd 100644 --- a/Src/Notion.Client/Models/PropertyItems/ListPropertyItem.cs +++ b/Src/Notion.Client/Models/PropertyItems/ListPropertyItem.cs @@ -1,17 +1,13 @@ using System.Collections.Generic; -using JsonSubTypes; +using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; namespace Notion.Client { - [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(RollupPropertyItem), "rollup")] - [JsonSubtypes.FallBackSubType(typeof(ListPropertyItem))] + [SuppressMessage("ReSharper", "ClassWithVirtualMembersNeverInherited.Global")] + [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")] public class ListPropertyItem : IPropertyItemObject { - public string Object => "list"; - public virtual string Type { get; set; } - [JsonProperty("results")] public IEnumerable Results { get; set; } @@ -20,5 +16,16 @@ public class ListPropertyItem : IPropertyItemObject [JsonProperty("next_cursor")] public string NextCursor { get; set; } + + [JsonProperty("property_item")] + public SimplePropertyItem PropertyItem { get; set; } + + public string Object => "list"; + + public virtual string Type { get; set; } + + public string Id { get; set; } + + public string NextURL { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyItems/NumberPropertyItem.cs b/Src/Notion.Client/Models/PropertyItems/NumberPropertyItem.cs index 144fc627..ea2ab0ad 100644 --- a/Src/Notion.Client/Models/PropertyItems/NumberPropertyItem.cs +++ b/Src/Notion.Client/Models/PropertyItems/NumberPropertyItem.cs @@ -7,6 +7,6 @@ public class NumberPropertyItem : SimplePropertyItem public override string Type => "number"; [JsonProperty("number")] - public Number Number { get; set; } + public double? Number { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyItems/RichTextPropertyItem.cs b/Src/Notion.Client/Models/PropertyItems/RichTextPropertyItem.cs index 513b7300..f9ce925a 100644 --- a/Src/Notion.Client/Models/PropertyItems/RichTextPropertyItem.cs +++ b/Src/Notion.Client/Models/PropertyItems/RichTextPropertyItem.cs @@ -1,9 +1,12 @@ -namespace Notion.Client +using Newtonsoft.Json; + +namespace Notion.Client { public class RichTextPropertyItem : SimplePropertyItem { public override string Type => "rich_text"; + [JsonProperty("rich_text")] public RichTextBase RichText { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyItems/RollupPropertyItem.cs b/Src/Notion.Client/Models/PropertyItems/RollupPropertyItem.cs index 6e6e53cd..04d7da7b 100644 --- a/Src/Notion.Client/Models/PropertyItems/RollupPropertyItem.cs +++ b/Src/Notion.Client/Models/PropertyItems/RollupPropertyItem.cs @@ -1,12 +1,37 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using Newtonsoft.Json; namespace Notion.Client { - public class RollupPropertyItem : ListPropertyItem + public class RollupPropertyItem : SimplePropertyItem { public override string Type => "rollup"; [JsonProperty("rollup")] - public RollupValue Rollup { get; set; } + public Data Rollup { get; set; } + + public class Data + { + [JsonProperty("type")] + public string Type { get; set; } + + [JsonProperty("function")] + public string Function { get; set; } + + [JsonProperty("number")] + public double? Number { get; set; } + + [JsonProperty("date")] + public Date Date { get; set; } + + [JsonProperty("array")] + public IEnumerable> Array { get; set; } + + [JsonProperty("unsupported")] + public Dictionary Unsupported { get; set; } + + [JsonProperty("incomplete")] + public Dictionary Incomplete { get; set; } + } } } diff --git a/Src/Notion.Client/Models/PropertyItems/SimplePropertyItem.cs b/Src/Notion.Client/Models/PropertyItems/SimplePropertyItem.cs index 6cc1f52b..6ba77c67 100644 --- a/Src/Notion.Client/Models/PropertyItems/SimplePropertyItem.cs +++ b/Src/Notion.Client/Models/PropertyItems/SimplePropertyItem.cs @@ -1,31 +1,39 @@ -using JsonSubTypes; +using System.Diagnostics.CodeAnalysis; +using JsonSubTypes; using Newtonsoft.Json; namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(NumberPropertyItem), "number")] - [JsonSubtypes.KnownSubType(typeof(UrlPropertyItem), "url")] - [JsonSubtypes.KnownSubType(typeof(SelectPropertyItem), "select")] - [JsonSubtypes.KnownSubType(typeof(MultiSelectPropertyItem), "multi_select")] - [JsonSubtypes.KnownSubType(typeof(DatePropertyItem), "date")] - [JsonSubtypes.KnownSubType(typeof(EmailPropertyItem), "email")] - [JsonSubtypes.KnownSubType(typeof(PhoneNumberPropertyItem), "phone_number")] - [JsonSubtypes.KnownSubType(typeof(CheckboxPropertyItem), "checkbox")] - [JsonSubtypes.KnownSubType(typeof(FilesPropertyItem), "files")] - [JsonSubtypes.KnownSubType(typeof(CreatedByPropertyItem), "created_by")] - [JsonSubtypes.KnownSubType(typeof(CreatedTimePropertyItem), "created_time")] - [JsonSubtypes.KnownSubType(typeof(LastEditedByPropertyItem), "last_edited_by")] - [JsonSubtypes.KnownSubType(typeof(LastEditedTimePropertyItem), "last_edited_time")] - [JsonSubtypes.KnownSubType(typeof(FormulaPropertyItem), "formula")] - [JsonSubtypes.KnownSubType(typeof(TitlePropertyItem), "title")] - [JsonSubtypes.KnownSubType(typeof(RichTextPropertyItem), "rich_text")] - [JsonSubtypes.KnownSubType(typeof(PeoplePropertyItem), "people")] - [JsonSubtypes.KnownSubType(typeof(RelationPropertyItem), "relation")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(NumberPropertyItem), "number")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(UrlPropertyItem), "url")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(SelectPropertyItem), "select")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(MultiSelectPropertyItem), "multi_select")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(StatusPropertyItem), "status")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(DatePropertyItem), "date")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(EmailPropertyItem), "email")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(PhoneNumberPropertyItem), "phone_number")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CheckboxPropertyItem), "checkbox")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(FilesPropertyItem), "files")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CreatedByPropertyItem), "created_by")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CreatedTimePropertyItem), "created_time")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(LastEditedByPropertyItem), "last_edited_by")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(LastEditedTimePropertyItem), "last_edited_time")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(FormulaPropertyItem), "formula")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(TitlePropertyItem), "title")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RichTextPropertyItem), "rich_text")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(PeoplePropertyItem), "people")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RelationPropertyItem), "relation")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RollupPropertyItem), "rollup")] + [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Global")] public abstract class SimplePropertyItem : IPropertyItemObject { public string Object => "property_item"; public abstract string Type { get; } + + public string Id { get; set; } + + public string NextURL { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyItems/StatusPropertyItem.cs b/Src/Notion.Client/Models/PropertyItems/StatusPropertyItem.cs new file mode 100644 index 00000000..6b6b863c --- /dev/null +++ b/Src/Notion.Client/Models/PropertyItems/StatusPropertyItem.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class StatusPropertyItem : SimplePropertyItem + { + public override string Type => "status"; + + [JsonProperty("status")] + public SelectOption Status { get; set; } + } +} diff --git a/Src/Notion.Client/Models/PropertyValue/ButtonPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/ButtonPropertyValue.cs new file mode 100644 index 00000000..61ae9fc4 --- /dev/null +++ b/Src/Notion.Client/Models/PropertyValue/ButtonPropertyValue.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class ButtonPropertyValue : PropertyValue + { + public override PropertyValueType Type => PropertyValueType.Button; + + [JsonProperty("button")] + public ButtonValue Button { get; set; } + + public class ButtonValue { } + } +} diff --git a/Src/Notion.Client/Models/PropertyValue/CheckboxPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/CheckboxPropertyValue.cs index 1f3fec50..422eb9b7 100644 --- a/Src/Notion.Client/Models/PropertyValue/CheckboxPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/CheckboxPropertyValue.cs @@ -3,7 +3,7 @@ namespace Notion.Client { /// - /// Checkbox property value objects contain a boolean within the checkbox property. + /// Checkbox property value objects contain a boolean within the checkbox property. /// public class CheckboxPropertyValue : PropertyValue { diff --git a/Src/Notion.Client/Models/PropertyValue/CreatedByPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/CreatedByPropertyValue.cs index 17fa4ef4..31cf5f5b 100644 --- a/Src/Notion.Client/Models/PropertyValue/CreatedByPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/CreatedByPropertyValue.cs @@ -3,14 +3,14 @@ namespace Notion.Client { /// - /// Created by property value object + /// Created by property value object /// public class CreatedByPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.CreatedBy; /// - /// Describes the user who created this page. + /// Describes the user who created this page. /// [JsonProperty("created_by")] public User CreatedBy { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/CreatedTimePropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/CreatedTimePropertyValue.cs index 0b20b2dc..e0550d13 100644 --- a/Src/Notion.Client/Models/PropertyValue/CreatedTimePropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/CreatedTimePropertyValue.cs @@ -3,14 +3,14 @@ namespace Notion.Client { /// - /// Created time property value object. + /// Created time property value object. /// public class CreatedTimePropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.CreatedTime; /// - /// The date and time when this page was created. + /// The date and time when this page was created. /// [JsonProperty("created_time")] public string CreatedTime { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs b/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs new file mode 100644 index 00000000..37b84bc5 --- /dev/null +++ b/Src/Notion.Client/Models/PropertyValue/DateCustomConverter.cs @@ -0,0 +1,81 @@ +using System; +using System.Globalization; +using Newtonsoft.Json; + +namespace Notion.Client +{ + public class DateCustomConverter : JsonConverter + { + private const string DateFormat = "yyyy-MM-dd"; + private const string DateTimeFormat = "yyyy-MM-ddTHH:mm:ssZ"; + + public override Date ReadJson(JsonReader reader, Type objectType, Date existingValue, bool hasExistingValue, + JsonSerializer serializer) + { + var jsonObject = serializer.Deserialize(reader); + + if (jsonObject == null) + { + return null; + } + + var date = new Date + { + Start = ParseDateTime(jsonObject.Start, out bool includeTime), + End = ParseDateTime(jsonObject.End, out _), + TimeZone = jsonObject.TimeZone, + IncludeTime = includeTime, + }; + + return date; + } + + public override void WriteJson(JsonWriter writer, Date value, JsonSerializer serializer) + { + if (value is null) + { + writer.WriteNull(); + + return; + } + + writer.WriteStartObject(); + + if (value.Start.HasValue) + { + string startFormat = value.IncludeTime ? DateTimeFormat : DateFormat; + writer.WritePropertyName("start"); + writer.WriteValue(value.Start.Value.ToString(startFormat, CultureInfo.InvariantCulture)); + } + + if (value.End.HasValue) + { + string endFormat = value.IncludeTime ? DateTimeFormat : DateFormat; + writer.WritePropertyName("end"); + writer.WriteValue(value.End.Value.ToString(endFormat, CultureInfo.InvariantCulture)); + } + + if (!string.IsNullOrEmpty(value.TimeZone)) + { + writer.WritePropertyName("time_zone"); + writer.WriteValue(value.TimeZone); + } + + writer.WriteEndObject(); + } + + private static DateTime? ParseDateTime(string dateTimeString, out bool includeTime) + { + includeTime = false; + + if (string.IsNullOrEmpty(dateTimeString)) + { + return null; + } + + includeTime = dateTimeString.Contains("T") || dateTimeString.Contains(" "); + + return DateTimeOffset.Parse(dateTimeString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).UtcDateTime; + } + } +} diff --git a/Src/Notion.Client/Models/PropertyValue/DateJsonObject.cs b/Src/Notion.Client/Models/PropertyValue/DateJsonObject.cs new file mode 100644 index 00000000..b33b58c7 --- /dev/null +++ b/Src/Notion.Client/Models/PropertyValue/DateJsonObject.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + internal class DateJsonObject + { + [JsonProperty("start")] + public string Start { get; set; } + + [JsonProperty("end")] + public string End { get; set; } + + [JsonProperty("time_zone")] + public string TimeZone { get; set; } + } +} diff --git a/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs index 6182b197..66f9c6b6 100644 --- a/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/DatePropertyValue.cs @@ -1,37 +1,54 @@ using System; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; namespace Notion.Client { /// - /// Date property value object. + /// Date property value object. /// public class DatePropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.Date; /// - /// Date + /// Date /// - [JsonProperty("date")] + [JsonProperty("date", NullValueHandling = NullValueHandling.Include)] public Date Date { get; set; } } /// - /// Date value object. + /// Date value object. /// + [JsonConverter(typeof(DateCustomConverter))] public class Date { /// - /// Start date with optional time. + /// Start date with optional time. /// [JsonProperty("start")] - public DateTime? Start { get; set; } + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTimeOffset? Start { get; set; } /// - /// End date with optional time. + /// End date with optional time. /// [JsonProperty("end")] - public DateTime? End { get; set; } + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTimeOffset? End { get; set; } + + /// + /// Optional time zone information for start and end. Possible values are extracted from the IANA database and they are + /// based on the time zones from Moment.js. + /// + [JsonProperty("time_zone")] + public string TimeZone { get; set; } + + /// + /// Whether to include time + /// + [JsonIgnore] + public bool IncludeTime { get; set; } = true; } } diff --git a/Src/Notion.Client/Models/PropertyValue/EmailPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/EmailPropertyValue.cs index 1869fc85..a8ac4806 100644 --- a/Src/Notion.Client/Models/PropertyValue/EmailPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/EmailPropertyValue.cs @@ -3,16 +3,16 @@ namespace Notion.Client { /// - /// Email property value object. + /// Email property value object. /// public class EmailPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.Email; /// - /// Describes an email address. + /// Describes an email address. /// - [JsonProperty("email")] + [JsonProperty("email", NullValueHandling = NullValueHandling.Include)] public string Email { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyValue/FilesPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/FilesPropertyValue.cs index 41580fdb..67bc864a 100644 --- a/Src/Notion.Client/Models/PropertyValue/FilesPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/FilesPropertyValue.cs @@ -4,14 +4,14 @@ namespace Notion.Client { /// - /// File property value object. + /// File property value object. /// public class FilesPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.Files; /// - /// Array of File Object with name. + /// Array of File Object with name. /// [JsonProperty("files")] public List Files { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/FormulaPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/FormulaPropertyValue.cs index e85fd5bd..a1242f47 100644 --- a/Src/Notion.Client/Models/PropertyValue/FormulaPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/FormulaPropertyValue.cs @@ -3,50 +3,50 @@ namespace Notion.Client { /// - /// Formula property value object. + /// Formula property value object. /// public class FormulaPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.Formula; /// - /// A formula described in the database's properties. + /// A formula described in the database's properties. /// [JsonProperty("formula")] public FormulaValue Formula { get; set; } } /// - /// Formula value object. + /// Formula value object. /// public class FormulaValue { /// - /// Formula value type + /// Formula value type /// [JsonProperty("type")] public string Type { get; set; } /// - /// String formula value. + /// String formula value. /// [JsonProperty("string")] public string String { get; set; } /// - /// Number formula value. + /// Number formula value. /// [JsonProperty("number")] public double? Number { get; set; } /// - /// Boolean formula value. + /// Boolean formula value. /// [JsonProperty("boolean")] public bool? Boolean { get; set; } /// - /// Date formula value + /// Date formula value /// [JsonProperty("date")] public Date Date { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/LastEditedByPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/LastEditedByPropertyValue.cs index b5e26efc..a536ad60 100644 --- a/Src/Notion.Client/Models/PropertyValue/LastEditedByPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/LastEditedByPropertyValue.cs @@ -3,14 +3,14 @@ namespace Notion.Client { /// - /// Last edited by property value object. + /// Last edited by property value object. /// public class LastEditedByPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.LastEditedBy; /// - /// Describes the user who last updated this page. + /// Describes the user who last updated this page. /// [JsonProperty("last_edited_by")] public User LastEditedBy { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/LastEditedTimePropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/LastEditedTimePropertyValue.cs index 785ac3bc..25d3100d 100644 --- a/Src/Notion.Client/Models/PropertyValue/LastEditedTimePropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/LastEditedTimePropertyValue.cs @@ -3,14 +3,14 @@ namespace Notion.Client { /// - /// Last edited time property value object. + /// Last edited time property value object. /// public class LastEditedTimePropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.LastEditedTime; /// - /// The date and time when this page was last updated. + /// The date and time when this page was last updated. /// [JsonProperty("last_edited_time")] public string LastEditedTime { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/MultiSelectPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/MultiSelectPropertyValue.cs index a5ee4f0a..ff820527 100644 --- a/Src/Notion.Client/Models/PropertyValue/MultiSelectPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/MultiSelectPropertyValue.cs @@ -4,14 +4,14 @@ namespace Notion.Client { /// - /// Multi-select property value object. + /// Multi-select property value object. /// public class MultiSelectPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.MultiSelect; /// - /// An array of multi-select option values. + /// An array of multi-select option values. /// [JsonProperty("multi_select")] public List MultiSelect { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/NumberPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/NumberPropertyValue.cs index a98a34d4..a709e4f5 100644 --- a/Src/Notion.Client/Models/PropertyValue/NumberPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/NumberPropertyValue.cs @@ -3,16 +3,16 @@ namespace Notion.Client { /// - /// Number formula property value object. + /// Number formula property value object. /// public class NumberPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.Number; /// - /// Value of number + /// Value of number /// - [JsonProperty("number")] + [JsonProperty("number", NullValueHandling = NullValueHandling.Include)] public double? Number { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyValue/PeoplePropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/PeoplePropertyValue.cs index 047d33ee..62557560 100644 --- a/Src/Notion.Client/Models/PropertyValue/PeoplePropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/PeoplePropertyValue.cs @@ -4,14 +4,14 @@ namespace Notion.Client { /// - /// People property value object. + /// People property value object. /// public class PeoplePropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.People; /// - /// List of users. + /// List of users. /// [JsonProperty("people")] public List People { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/PhoneNumberPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/PhoneNumberPropertyValue.cs index be5e482e..f7c37a19 100644 --- a/Src/Notion.Client/Models/PropertyValue/PhoneNumberPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/PhoneNumberPropertyValue.cs @@ -3,16 +3,16 @@ namespace Notion.Client { /// - /// Phone number property value object. + /// Phone number property value object. /// public class PhoneNumberPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.PhoneNumber; /// - /// Phone number value + /// Phone number value /// - [JsonProperty("phone_number")] + [JsonProperty("phone_number", NullValueHandling = NullValueHandling.Include)] public string PhoneNumber { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyValue/PropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/PropertyValue.cs index c85ebbdc..e1289046 100644 --- a/Src/Notion.Client/Models/PropertyValue/PropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/PropertyValue.cs @@ -1,36 +1,43 @@ -using JsonSubTypes; +using System.Diagnostics.CodeAnalysis; +using JsonSubTypes; using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace Notion.Client { /// - /// An object describing the identifier, type, and value of a page property. + /// An object describing the identifier, type, and value of a page property. /// [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(CheckboxPropertyValue), PropertyValueType.Checkbox)] - [JsonSubtypes.KnownSubType(typeof(CreatedByPropertyValue), PropertyValueType.CreatedBy)] - [JsonSubtypes.KnownSubType(typeof(CreatedTimePropertyValue), PropertyValueType.CreatedTime)] - [JsonSubtypes.KnownSubType(typeof(DatePropertyValue), PropertyValueType.Date)] - [JsonSubtypes.KnownSubType(typeof(EmailPropertyValue), PropertyValueType.Email)] - [JsonSubtypes.KnownSubType(typeof(FilesPropertyValue), PropertyValueType.Files)] - [JsonSubtypes.KnownSubType(typeof(FormulaPropertyValue), PropertyValueType.Formula)] - [JsonSubtypes.KnownSubType(typeof(LastEditedByPropertyValue), PropertyValueType.LastEditedBy)] - [JsonSubtypes.KnownSubType(typeof(LastEditedTimePropertyValue), PropertyValueType.LastEditedTime)] - [JsonSubtypes.KnownSubType(typeof(MultiSelectPropertyValue), PropertyValueType.MultiSelect)] - [JsonSubtypes.KnownSubType(typeof(NumberPropertyValue), PropertyValueType.Number)] - [JsonSubtypes.KnownSubType(typeof(PeoplePropertyValue), PropertyValueType.People)] - [JsonSubtypes.KnownSubType(typeof(PhoneNumberPropertyValue), PropertyValueType.PhoneNumber)] - [JsonSubtypes.KnownSubType(typeof(RelationPropertyValue), PropertyValueType.Relation)] - [JsonSubtypes.KnownSubType(typeof(RichTextPropertyValue), PropertyValueType.RichText)] - [JsonSubtypes.KnownSubType(typeof(RollupPropertyValue), PropertyValueType.Rollup)] - [JsonSubtypes.KnownSubType(typeof(SelectPropertyValue), PropertyValueType.Select)] - [JsonSubtypes.KnownSubType(typeof(TitlePropertyValue), PropertyValueType.Title)] - [JsonSubtypes.KnownSubType(typeof(UrlPropertyValue), PropertyValueType.Url)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CheckboxPropertyValue), PropertyValueType.Checkbox)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CreatedByPropertyValue), PropertyValueType.CreatedBy)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(CreatedTimePropertyValue), PropertyValueType.CreatedTime)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(DatePropertyValue), PropertyValueType.Date)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(EmailPropertyValue), PropertyValueType.Email)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(FilesPropertyValue), PropertyValueType.Files)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(FormulaPropertyValue), PropertyValueType.Formula)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(LastEditedByPropertyValue), PropertyValueType.LastEditedBy)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(LastEditedTimePropertyValue), PropertyValueType.LastEditedTime)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(MultiSelectPropertyValue), PropertyValueType.MultiSelect)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(NumberPropertyValue), PropertyValueType.Number)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(PeoplePropertyValue), PropertyValueType.People)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(PhoneNumberPropertyValue), PropertyValueType.PhoneNumber)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RelationPropertyValue), PropertyValueType.Relation)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RichTextPropertyValue), PropertyValueType.RichText)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(RollupPropertyValue), PropertyValueType.Rollup)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(SelectPropertyValue), PropertyValueType.Select)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(StatusPropertyValue), PropertyValueType.Status)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(TitlePropertyValue), PropertyValueType.Title)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(UrlPropertyValue), PropertyValueType.Url)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(UniqueIdPropertyValue), PropertyValueType.UniqueId)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(ButtonPropertyValue), PropertyValueType.Button)] + [JsonSubtypes.KnownSubTypeAttribute(typeof(VerificationPropertyValue), PropertyValueType.Verification)] + [SuppressMessage("ReSharper", "UnusedMember.Global")] + [SuppressMessage("ReSharper", "UnassignedGetOnlyAutoProperty")] public class PropertyValue { /// - /// Underlying identifier of the property. + /// Underlying identifier of the property. /// [JsonProperty("id")] public string Id { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/PropertyValueType.cs b/Src/Notion.Client/Models/PropertyValue/PropertyValueType.cs index 4a4b8189..574138e9 100644 --- a/Src/Notion.Client/Models/PropertyValue/PropertyValueType.cs +++ b/Src/Notion.Client/Models/PropertyValue/PropertyValueType.cs @@ -1,10 +1,12 @@ -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; namespace Notion.Client { /// - /// Types of Property Value + /// Types of Property Value /// + [SuppressMessage("ReSharper", "UnusedMember.Global")] public enum PropertyValueType { [EnumMember(Value = null)] @@ -65,6 +67,18 @@ public enum PropertyValueType LastEditedTime, [EnumMember(Value = "last_edited_by")] - LastEditedBy + LastEditedBy, + + [EnumMember(Value = "status")] + Status, + + [EnumMember(Value = "unique_id")] + UniqueId, + + [EnumMember(Value = "verification")] + Verification, + + [EnumMember(Value = "button")] + Button, } } diff --git a/Src/Notion.Client/Models/PropertyValue/RelationPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/RelationPropertyValue.cs index 3d11a096..6f9e0ec8 100644 --- a/Src/Notion.Client/Models/PropertyValue/RelationPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/RelationPropertyValue.cs @@ -4,14 +4,14 @@ namespace Notion.Client { /// - /// Relation property value object. + /// Relation property value object. /// public class RelationPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.Relation; /// - /// Array of page references + /// Array of page references /// [JsonProperty("relation")] public List Relation { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/RichTextPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/RichTextPropertyValue.cs index 028ed046..90e8d0be 100644 --- a/Src/Notion.Client/Models/PropertyValue/RichTextPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/RichTextPropertyValue.cs @@ -4,14 +4,14 @@ namespace Notion.Client { /// - /// Rich Text property value object. + /// Rich Text property value object. /// public class RichTextPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.RichText; /// - /// List of rich text objects + /// List of rich text objects /// [JsonProperty("rich_text")] public List RichText { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/RollupPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/RollupPropertyValue.cs index 7c573081..92020c61 100644 --- a/Src/Notion.Client/Models/PropertyValue/RollupPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/RollupPropertyValue.cs @@ -4,7 +4,7 @@ namespace Notion.Client { /// - /// Rollup property value object. + /// Rollup property value object. /// public class RollupPropertyValue : PropertyValue { @@ -15,31 +15,31 @@ public class RollupPropertyValue : PropertyValue } /// - /// Object containing rollup type-specific data. + /// Object containing rollup type-specific data. /// public class RollupValue { /// - /// The type of rollup. Possible values are "number", "date", and "array". + /// The type of rollup. Possible values are "number", "date", and "array". /// [JsonProperty("type")] public string Type { get; set; } /// - /// Number rollup property values contain a number + /// Number rollup property values contain a number /// [JsonProperty("number")] public double? Number { get; set; } /// - /// Date rollup property values contain a date property value. + /// Date rollup property values contain a date property value. /// [JsonProperty("date")] - public DatePropertyValue Date { get; set; } + public Date Date { get; set; } /// - /// Array rollup property values contain an array of element objects. - /// Array containing the property value object will not contain value for Id field + /// Array rollup property values contain an array of element objects. + /// Array containing the property value object will not contain value for Id field /// [JsonProperty("array")] public List Array { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/SelectPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/SelectPropertyValue.cs index 07b2a346..63d333d9 100644 --- a/Src/Notion.Client/Models/PropertyValue/SelectPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/SelectPropertyValue.cs @@ -3,13 +3,13 @@ namespace Notion.Client { /// - /// Select property value object. + /// Select property value object. /// public class SelectPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.Select; - [JsonProperty("select")] + [JsonProperty("select", NullValueHandling = NullValueHandling.Include)] public SelectOption Select { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyValue/StatusPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/StatusPropertyValue.cs new file mode 100644 index 00000000..921ff810 --- /dev/null +++ b/Src/Notion.Client/Models/PropertyValue/StatusPropertyValue.cs @@ -0,0 +1,75 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Notion.Client +{ + /// + /// Status property value objects contain page status + /// + public class StatusPropertyValue : PropertyValue + { + [SuppressMessage("ReSharper", "UnusedMember.Global")] + [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] + public enum StatusColor + { + [EnumMember(Value = "default")] + Default, + + [EnumMember(Value = "gray")] + Gray, + + [EnumMember(Value = "brown")] + Brown, + + [EnumMember(Value = "orange")] + Orange, + + [EnumMember(Value = "yellow")] + Yellow, + + [EnumMember(Value = "green")] + Green, + + [EnumMember(Value = "blue")] + Blue, + + [EnumMember(Value = "purple")] + Purple, + + [EnumMember(Value = "pink")] + Pink, + + [EnumMember(Value = "red")] + Red + } + + public override PropertyValueType Type => PropertyValueType.Status; + + [JsonProperty("status", NullValueHandling = NullValueHandling.Include)] + public Data Status { get; set; } + + public class Data + { + /// + /// ID of the option. + /// + [JsonProperty("id")] + public string Id { get; set; } + + /// + /// Name of the option as it appears in Notion. + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// Color of the option. + /// + [JsonProperty("color")] + [JsonConverter(typeof(StringEnumConverter))] + public StatusColor? Color { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/PropertyValue/TitlePropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/TitlePropertyValue.cs index 58b7842d..e72a2a08 100644 --- a/Src/Notion.Client/Models/PropertyValue/TitlePropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/TitlePropertyValue.cs @@ -4,14 +4,14 @@ namespace Notion.Client { /// - /// Title property value object. + /// Title property value object. /// public class TitlePropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.Title; /// - /// An array of rich text objects + /// An array of rich text objects /// [JsonProperty("title")] public List Title { get; set; } diff --git a/Src/Notion.Client/Models/PropertyValue/UniqueIdPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/UniqueIdPropertyValue.cs new file mode 100644 index 00000000..0ce1c704 --- /dev/null +++ b/Src/Notion.Client/Models/PropertyValue/UniqueIdPropertyValue.cs @@ -0,0 +1,28 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + /// + /// Unique Id property value object. + /// + public class UniqueIdPropertyValue : PropertyValue + { + public override PropertyValueType Type => PropertyValueType.UniqueId; + + /// + /// Unique Id property of database item + /// + [JsonProperty("unique_id")] + public UniqueIdValue UniqueId { get; set; } + } + + public class UniqueIdValue + { + [JsonProperty("prefix")] + public string Prefix { get; set; } + + [JsonProperty("number")] + public double? Number { get; set; } + } +} + diff --git a/Src/Notion.Client/Models/PropertyValue/UrlPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/UrlPropertyValue.cs index 08b800e6..91b5f71c 100644 --- a/Src/Notion.Client/Models/PropertyValue/UrlPropertyValue.cs +++ b/Src/Notion.Client/Models/PropertyValue/UrlPropertyValue.cs @@ -3,16 +3,16 @@ namespace Notion.Client { /// - /// URL property value object. + /// URL property value object. /// public class UrlPropertyValue : PropertyValue { public override PropertyValueType Type => PropertyValueType.Url; /// - /// Describes a web address + /// Describes a web address /// - [JsonProperty("url")] + [JsonProperty("url", NullValueHandling = NullValueHandling.Include)] public string Url { get; set; } } } diff --git a/Src/Notion.Client/Models/PropertyValue/VerificationPropertyValue.cs b/Src/Notion.Client/Models/PropertyValue/VerificationPropertyValue.cs new file mode 100644 index 00000000..8cdb9bae --- /dev/null +++ b/Src/Notion.Client/Models/PropertyValue/VerificationPropertyValue.cs @@ -0,0 +1,39 @@ +using Newtonsoft.Json; + +namespace Notion.Client +{ + /// + /// Verification property value. + /// + public class VerificationPropertyValue : PropertyValue + { + public override PropertyValueType Type => PropertyValueType.Verification; + + /// + /// Object containing verification type-specific data. + /// + [JsonProperty("verification")] + public Info Verification { get; set; } + + public class Info + { + /// + /// The state of verification. Possible values are "verified" and "unverified". + /// + [JsonProperty("state")] + public string State { get; set; } + + /// + /// Describes the user who verified this page. + /// + [JsonProperty("verified_by")] + public User VerifiedBy { get; set; } + + /// + /// Date verification property values contain a date property value. + /// + [JsonProperty("date")] + public Date Date { get; set; } + } + } +} diff --git a/Src/Notion.Client/Models/User/BotOwner/IBotOwner.cs b/Src/Notion.Client/Models/User/BotOwner/IBotOwner.cs index 61a7a887..062d75c0 100644 --- a/Src/Notion.Client/Models/User/BotOwner/IBotOwner.cs +++ b/Src/Notion.Client/Models/User/BotOwner/IBotOwner.cs @@ -4,8 +4,8 @@ namespace Notion.Client { [JsonConverter(typeof(JsonSubtypes), "type")] - [JsonSubtypes.KnownSubType(typeof(UserOwner), "user")] - [JsonSubtypes.KnownSubType(typeof(WorkspaceIntegrationOwner), "workspace")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(UserOwner), "user")] + [JsonSubtypes.KnownSubTypeAttribute(typeof(WorkspaceIntegrationOwner), "workspace")] public interface IBotOwner { [JsonProperty("type")] diff --git a/Src/Notion.Client/Models/User/BotOwner/UserOwner.cs b/Src/Notion.Client/Models/User/BotOwner/UserOwner.cs index 40d69867..e60a4fab 100644 --- a/Src/Notion.Client/Models/User/BotOwner/UserOwner.cs +++ b/Src/Notion.Client/Models/User/BotOwner/UserOwner.cs @@ -4,9 +4,9 @@ namespace Notion.Client { public class UserOwner : IBotOwner { - public string Type { get; set; } - [JsonProperty("user")] public User User { get; set; } + + public string Type { get; set; } } } diff --git a/Src/Notion.Client/Models/User/BotOwner/WorkspaceIntegrationOwner.cs b/Src/Notion.Client/Models/User/BotOwner/WorkspaceIntegrationOwner.cs index 78a84f6f..b5304157 100644 --- a/Src/Notion.Client/Models/User/BotOwner/WorkspaceIntegrationOwner.cs +++ b/Src/Notion.Client/Models/User/BotOwner/WorkspaceIntegrationOwner.cs @@ -4,9 +4,9 @@ namespace Notion.Client { public class WorkspaceIntegrationOwner : IBotOwner { - public string Type { get; set; } - [JsonProperty("workspace")] public bool Workspace { get; set; } + + public string Type { get; set; } } } diff --git a/Src/Notion.Client/Models/User/PartialUser.cs b/Src/Notion.Client/Models/User/PartialUser.cs new file mode 100644 index 00000000..7e74c6d8 --- /dev/null +++ b/Src/Notion.Client/Models/User/PartialUser.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Notion.Client +{ + [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] + public class PartialUser : IObject + { + public string Id { get; set; } + + public ObjectType Object => ObjectType.User; + } +} diff --git a/Src/Notion.Client/Models/User/User.cs b/Src/Notion.Client/Models/User/User.cs index 7f336929..6ae9efef 100644 --- a/Src/Notion.Client/Models/User/User.cs +++ b/Src/Notion.Client/Models/User/User.cs @@ -4,9 +4,6 @@ namespace Notion.Client { public class User : IObject { - public ObjectType Object => ObjectType.User; - public string Id { get; set; } - [JsonProperty("type")] public string Type { get; set; } @@ -21,5 +18,9 @@ public class User : IObject [JsonProperty("bot")] public Bot Bot { get; set; } + + public ObjectType Object => ObjectType.User; + + public string Id { get; set; } } } diff --git a/Src/Notion.Client/Notion.Client.csproj b/Src/Notion.Client/Notion.Client.csproj index faaa9fb0..2a6555c3 100644 --- a/Src/Notion.Client/Notion.Client.csproj +++ b/Src/Notion.Client/Notion.Client.csproj @@ -1,9 +1,9 @@ - + - 2.2.1-preview + 4.3.0-preview netstandard2.0 - 7.3 + 9.0 Notion.Net Vedant Koditkar @@ -16,14 +16,13 @@ - - all + runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + diff --git a/Src/Notion.Client/NotionAPIErrorCode.cs b/Src/Notion.Client/NotionAPIErrorCode.cs index 209aef11..2ca2573f 100644 --- a/Src/Notion.Client/NotionAPIErrorCode.cs +++ b/Src/Notion.Client/NotionAPIErrorCode.cs @@ -1,18 +1,23 @@ -using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMember.Global")] public enum NotionAPIErrorCode { [EnumMember(Value = "invalid_json")] InvalidJSON, [EnumMember(Value = "invalid_request_url")] - InvalidRequestURL, + InvalidRequestUrl, [EnumMember(Value = "invalid_request")] InvalidRequest, + [EnumMember(Value = "invalid_grant")] + InvalidGrant, + [EnumMember(Value = "validation_error")] ValidationError, @@ -37,7 +42,16 @@ public enum NotionAPIErrorCode [EnumMember(Value = "internal_server_error")] InternalServerError, + [EnumMember(Value = "bad_gateway")] + BadGateway, + [EnumMember(Value = "service_unavailable")] ServiceUnavailable, + + [EnumMember(Value = "database_connection_unavailable")] + DatabaseConnectionUnavailable, + + [EnumMember(Value = "gateway_timeout")] + GatewayTimeout } } diff --git a/Src/Notion.Client/NotionApiErrorResponse.cs b/Src/Notion.Client/NotionApiErrorResponse.cs index f21c6923..44a225b2 100644 --- a/Src/Notion.Client/NotionApiErrorResponse.cs +++ b/Src/Notion.Client/NotionApiErrorResponse.cs @@ -1,8 +1,13 @@ -namespace Notion.Client +using Newtonsoft.Json; + +namespace Notion.Client { - class NotionApiErrorResponse + internal class NotionApiErrorResponse { + [JsonProperty("code")] public NotionAPIErrorCode ErrorCode { get; set; } + + [JsonProperty("message")] public string Message { get; set; } } } diff --git a/Src/Notion.Client/NotionApiException.cs b/Src/Notion.Client/NotionApiException.cs index 358db384..cf62f1b6 100644 --- a/Src/Notion.Client/NotionApiException.cs +++ b/Src/Notion.Client/NotionApiException.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Net; namespace Notion.Client { + [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] public class NotionApiException : Exception { public NotionApiException(HttpStatusCode statusCode, NotionAPIErrorCode? notionAPIErrorCode, string message) @@ -10,12 +12,7 @@ public NotionApiException(HttpStatusCode statusCode, NotionAPIErrorCode? notionA { } - public NotionApiException(HttpStatusCode statusCode, string message) - : this(statusCode, null, message, null) - { - } - - public NotionApiException( + private NotionApiException( HttpStatusCode statusCode, NotionAPIErrorCode? notionAPIErrorCode, string message, @@ -24,11 +21,17 @@ public NotionApiException( NotionAPIErrorCode = notionAPIErrorCode; StatusCode = statusCode; - Data.Add("StatusCode", statusCode); + InitializeData(); + } + + private void InitializeData() + { + Data.Add("StatusCode", StatusCode); Data.Add("NotionApiErrorCode", NotionAPIErrorCode); } public NotionAPIErrorCode? NotionAPIErrorCode { get; } + public HttpStatusCode StatusCode { get; } } } diff --git a/Src/Notion.Client/NotionApiRateLimitException.cs b/Src/Notion.Client/NotionApiRateLimitException.cs new file mode 100644 index 00000000..c10cce2d --- /dev/null +++ b/Src/Notion.Client/NotionApiRateLimitException.cs @@ -0,0 +1,22 @@ +using System; +using System.Net; + +namespace Notion.Client +{ + public sealed class NotionApiRateLimitException : NotionApiException + { + public TimeSpan? RetryAfter { get; } + + public NotionApiRateLimitException( + HttpStatusCode statusCode, + NotionAPIErrorCode? notionAPIErrorCode, + string message, + TimeSpan? retryAfter) + : base(statusCode, notionAPIErrorCode, message) + { + RetryAfter = retryAfter; + + Data.Add("RetryAfter", retryAfter); + } + } +} diff --git a/Src/Notion.Client/NotionClient.cs b/Src/Notion.Client/NotionClient.cs index 5bbc6dad..2fc6f479 100644 --- a/Src/Notion.Client/NotionClient.cs +++ b/Src/Notion.Client/NotionClient.cs @@ -1,38 +1,63 @@ -namespace Notion.Client +using System.Diagnostics.CodeAnalysis; + +namespace Notion.Client { + [SuppressMessage("ReSharper", "UnusedMemberInSuper.Global")] public interface INotionClient { + IAuthenticationClient AuthenticationClient { get; } + IUsersClient Users { get; } + IDatabasesClient Databases { get; } + IPagesClient Pages { get; } + ISearchClient Search { get; } + IBlocksClient Blocks { get; } + + ICommentsClient Comments { get; } + IRestClient RestClient { get; } } public class NotionClient : INotionClient { public NotionClient( - RestClient restClient, - UsersClient users, - DatabasesClient databases, - PagesClient pages, - SearchClient search, - BlocksClient blocks) + IRestClient restClient, + IUsersClient users, + IDatabasesClient databases, + IPagesClient pages, + ISearchClient search, + ICommentsClient comments, + IBlocksClient blocks, + IAuthenticationClient authenticationClient) { RestClient = restClient; Users = users; Databases = databases; Pages = pages; Search = search; + Comments = comments; Blocks = blocks; + AuthenticationClient = authenticationClient; } + public IAuthenticationClient AuthenticationClient { get; } + public IUsersClient Users { get; } + public IDatabasesClient Databases { get; } + public IPagesClient Pages { get; } + public ISearchClient Search { get; } + public IBlocksClient Blocks { get; } + + public ICommentsClient Comments { get; } + public IRestClient RestClient { get; } } } diff --git a/Src/Notion.Client/NotionClientFactory.cs b/Src/Notion.Client/NotionClientFactory.cs index 4010eb8c..e66f0781 100644 --- a/Src/Notion.Client/NotionClientFactory.cs +++ b/Src/Notion.Client/NotionClientFactory.cs @@ -7,12 +7,14 @@ public static NotionClient Create(ClientOptions options) var restClient = new RestClient(options); return new NotionClient( - restClient: restClient - , users: new UsersClient(restClient) - , databases: new DatabasesClient(restClient) - , pages: new PagesClient(restClient) - , search: new SearchClient(restClient) - , blocks: new BlocksClient(restClient) + restClient + , new UsersClient(restClient) + , new DatabasesClient(restClient) + , new PagesClient(restClient) + , new SearchClient(restClient) + , new CommentsClient(restClient) + , new BlocksClient(restClient) + , new AuthenticationClient(restClient) ); } } diff --git a/Src/Notion.Client/RestClient/ClientOptions.cs b/Src/Notion.Client/RestClient/ClientOptions.cs index 3c6be1bc..d0015767 100644 --- a/Src/Notion.Client/RestClient/ClientOptions.cs +++ b/Src/Notion.Client/RestClient/ClientOptions.cs @@ -3,7 +3,9 @@ public class ClientOptions { public string BaseUrl { get; set; } + public string NotionVersion { get; set; } + public string AuthToken { get; set; } } } diff --git a/Src/Notion.Client/RestClient/IRestClient.cs b/Src/Notion.Client/RestClient/IRestClient.cs index 792840b6..b8ffe5b3 100644 --- a/Src/Notion.Client/RestClient/IRestClient.cs +++ b/Src/Notion.Client/RestClient/IRestClient.cs @@ -17,7 +17,7 @@ Task GetAsync( Task PostAsync( string uri, object body, - IDictionary queryParams = null, + IEnumerable> queryParams = null, IDictionary headers = null, JsonSerializerSettings serializerSettings = null, CancellationToken cancellationToken = default); @@ -34,7 +34,6 @@ Task DeleteAsync( string uri, IDictionary queryParams = null, IDictionary headers = null, - JsonSerializerSettings serializerSettings = null, CancellationToken cancellationToken = default); } } diff --git a/Src/Notion.Client/RestClient/LoggingHandler.cs b/Src/Notion.Client/RestClient/LoggingHandler.cs index 8655bb97..a6001d10 100644 --- a/Src/Notion.Client/RestClient/LoggingHandler.cs +++ b/Src/Notion.Client/RestClient/LoggingHandler.cs @@ -7,7 +7,9 @@ namespace Notion.Client { public class LoggingHandler : DelegatingHandler { - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + protected override async Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken) { Log.Trace("Request: {request}", request); @@ -22,6 +24,7 @@ protected override async Task SendAsync(HttpRequestMessage catch (Exception ex) { Log.Error(ex, "Failed to get response: {exception}", ex); + throw; } } diff --git a/Src/Notion.Client/RestClient/RestClient.cs b/Src/Notion.Client/RestClient/RestClient.cs index b52b0ce3..59a848f7 100644 --- a/Src/Notion.Client/RestClient/RestClient.cs +++ b/Src/Notion.Client/RestClient/RestClient.cs @@ -14,68 +14,129 @@ namespace Notion.Client { public class RestClient : IRestClient { - private HttpClient _httpClient; private readonly ClientOptions _options; - protected readonly JsonSerializerSettings defaultSerializerSettings = new JsonSerializerSettings + protected readonly JsonSerializerSettings DefaultSerializerSettings = new() { NullValueHandling = NullValueHandling.Ignore, - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new CamelCaseNamingStrategy() - } + ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() } }; + private HttpClient _httpClient; + public RestClient(ClientOptions options) { _options = MergeOptions(options); } - private static ClientOptions MergeOptions(ClientOptions options) + public async Task GetAsync( + string uri, + IDictionary queryParams = null, + IDictionary headers = null, + JsonSerializerSettings serializerSettings = null, + CancellationToken cancellationToken = default) { - return new ClientOptions + var response = await SendAsync(uri, HttpMethod.Get, queryParams, headers, + cancellationToken: cancellationToken); + + return await response.ParseStreamAsync(serializerSettings); + } + + public async Task PostAsync( + string uri, + object body, + IEnumerable> queryParams = null, + IDictionary headers = null, + JsonSerializerSettings serializerSettings = null, + CancellationToken cancellationToken = default) + { + void AttachContent(HttpRequestMessage httpRequest) { - AuthToken = options.AuthToken, - BaseUrl = options.BaseUrl ?? Constants.BASE_URL, - NotionVersion = options.NotionVersion ?? Constants.DEFAULT_NOTION_VERSION - }; + httpRequest.Content = new StringContent(JsonConvert.SerializeObject(body, DefaultSerializerSettings), + Encoding.UTF8, "application/json"); + } + + var response = await SendAsync(uri, HttpMethod.Post, queryParams, headers, AttachContent, + cancellationToken); + + return await response.ParseStreamAsync(serializerSettings); } - public async Task GetAsync( + public async Task PatchAsync( string uri, + object body, IDictionary queryParams = null, IDictionary headers = null, JsonSerializerSettings serializerSettings = null, CancellationToken cancellationToken = default) { - var response = await SendAsync(uri, HttpMethod.Get, queryParams, headers, cancellationToken: cancellationToken); + void AttachContent(HttpRequestMessage httpRequest) + { + var serializedBody = JsonConvert.SerializeObject(body, DefaultSerializerSettings); + httpRequest.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json"); + } + + var response = await SendAsync(uri, new HttpMethod("PATCH"), queryParams, headers, AttachContent, + cancellationToken); return await response.ParseStreamAsync(serializerSettings); } + public async Task DeleteAsync( + string uri, + IDictionary queryParams = null, + IDictionary headers = null, + CancellationToken cancellationToken = default) + { + await SendAsync(uri, HttpMethod.Delete, queryParams, headers, null, cancellationToken); + } + + private static ClientOptions MergeOptions(ClientOptions options) + { + return new ClientOptions + { + AuthToken = options.AuthToken, + BaseUrl = options.BaseUrl ?? Constants.BaseUrl, + NotionVersion = options.NotionVersion ?? Constants.DefaultNotionVersion + }; + } + private static async Task BuildException(HttpResponseMessage response) { var errorBody = await response.Content.ReadAsStringAsync(); NotionApiErrorResponse errorResponse = null; + if (!string.IsNullOrWhiteSpace(errorBody)) { try { errorResponse = JsonConvert.DeserializeObject(errorBody); + + if (errorResponse.ErrorCode == NotionAPIErrorCode.RateLimited) + { + var retryAfter = response.Headers.RetryAfter.Delta; + return new NotionApiRateLimitException( + response.StatusCode, + errorResponse.ErrorCode, + errorResponse.Message, + retryAfter + ); + } } - catch + catch (Exception ex) { + Log.Error(ex, "Error when parsing the notion api response."); } } return new NotionApiException(response.StatusCode, errorResponse?.ErrorCode, errorResponse?.Message); } - public async Task SendAsync( + private async Task SendAsync( string requestUri, HttpMethod httpMethod, - IDictionary queryParams = null, + IEnumerable> queryParams = null, IDictionary headers = null, Action attachContent = null, CancellationToken cancellationToken = default) @@ -84,7 +145,7 @@ public async Task SendAsync( requestUri = AddQueryString(requestUri, queryParams); - HttpRequestMessage httpRequest = new HttpRequestMessage(httpMethod, requestUri); + using var httpRequest = new HttpRequestMessage(httpMethod, requestUri); httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.AuthToken); httpRequest.Headers.Add("Notion-Version", _options.NotionVersion); @@ -113,55 +174,20 @@ private static void AddHeaders(HttpRequestMessage request, IDictionary PostAsync( - string uri, - object body, - IDictionary queryParams = null, - IDictionary headers = null, - JsonSerializerSettings serializerSettings = null, - CancellationToken cancellationToken = default) + private void EnsureHttpClient() { - void AttachContent(HttpRequestMessage httpRequest) + if (_httpClient != null) { - httpRequest.Content = new StringContent(JsonConvert.SerializeObject(body, defaultSerializerSettings), Encoding.UTF8, "application/json"); + return; } - var response = await SendAsync(uri, HttpMethod.Post, queryParams, headers, AttachContent, cancellationToken: cancellationToken); - - return await response.ParseStreamAsync(serializerSettings); - } - - public async Task PatchAsync(string uri, object body, IDictionary queryParams = null, IDictionary headers = null, JsonSerializerSettings serializerSettings = null, CancellationToken cancellationToken = default) - { - void AttachContent(HttpRequestMessage httpRequest) - { - var serializedBody = JsonConvert.SerializeObject(body, defaultSerializerSettings); - httpRequest.Content = new StringContent(serializedBody, Encoding.UTF8, "application/json"); - } - - var response = await SendAsync(uri, new HttpMethod("PATCH"), queryParams, headers, AttachContent, cancellationToken: cancellationToken); - - return await response.ParseStreamAsync(serializerSettings); - } - - public async Task DeleteAsync(string uri, IDictionary queryParams = null, IDictionary headers = null, JsonSerializerSettings serializerSettings = null, CancellationToken cancellationToken = default) - { - await SendAsync(uri, HttpMethod.Delete, queryParams, headers, null, cancellationToken); - } - - private HttpClient EnsureHttpClient() - { - if (_httpClient == null) - { - var pipeline = new LoggingHandler() { InnerHandler = new HttpClientHandler() }; - _httpClient = new HttpClient(pipeline); - _httpClient.BaseAddress = new Uri(_options.BaseUrl); - } + var pipeline = new LoggingHandler { InnerHandler = new HttpClientHandler() }; - return _httpClient; + _httpClient = new HttpClient(pipeline); + _httpClient.BaseAddress = new Uri(_options.BaseUrl); } - private static string AddQueryString(string uri, IDictionary queryParams) + private static string AddQueryString(string uri, IEnumerable> queryParams) { return queryParams == null ? uri : QueryHelpers.AddQueryString(uri, queryParams); } diff --git a/Src/Notion.Client/http/QueryHelpers.cs b/Src/Notion.Client/http/QueryHelpers.cs index 783ae20e..60de4e7b 100644 --- a/Src/Notion.Client/http/QueryHelpers.cs +++ b/Src/Notion.Client/http/QueryHelpers.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net; using System.Text; @@ -8,6 +9,7 @@ namespace Notion.Client.http { internal static class QueryHelpers { + [SuppressMessage("ReSharper", "UnusedMember.Global")] public static string AddQueryString(string uri, string name, string value) { if (uri == null) @@ -43,7 +45,7 @@ public static string AddQueryString(string uri, IDictionary quer return AddQueryString(uri, (IEnumerable>)queryParams); } - private static string AddQueryString( + public static string AddQueryString( string uri, IEnumerable> queryParams) { @@ -74,6 +76,7 @@ private static string AddQueryString( var sb = new StringBuilder(); sb.Append(uriToBeAppended); + foreach (var parameter in queryParams) { sb.Append(hasQuery ? '&' : '?'); @@ -84,13 +87,14 @@ private static string AddQueryString( } sb.Append(anchorText); + return sb.ToString(); } - private static IEnumerable> RemoveEmptyValueQueryParams(IEnumerable> queryParams) + private static IEnumerable> RemoveEmptyValueQueryParams( + IEnumerable> queryParams) { return queryParams.Where(x => !string.IsNullOrWhiteSpace(x.Value)); } - } } diff --git a/Test/Notion.IntegrationTests/BlocksClientTests.cs b/Test/Notion.IntegrationTests/BlocksClientTests.cs new file mode 100644 index 00000000..5f1ca25b --- /dev/null +++ b/Test/Notion.IntegrationTests/BlocksClientTests.cs @@ -0,0 +1,453 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Notion.Client; +using Xunit; + +namespace Notion.IntegrationTests; + +public class IBlocksClientTests : IntegrationTestBase, IAsyncLifetime +{ + private Page _page = null!; + + public async Task InitializeAsync() + { + _page = await Client.Pages.CreateAsync( + PagesCreateParametersBuilder.Create( + new ParentPageInput { PageId = ParentPageId } + ).Build() + ); + } + + public async Task DisposeAsync() + { + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { InTrash = true }); + } + + [Fact] + public async Task AppendChildrenAsync_AppendsBlocksGivenBlocks() + { + var blocks = await Client.Blocks.AppendChildrenAsync( + new BlockAppendChildrenRequest + { + BlockId = _page.Id, + Children = new List + { + new BreadcrumbBlockRequest { Breadcrumb = new BreadcrumbBlockRequest.Data() }, + new DividerBlockRequest { Divider = new DividerBlockRequest.Data() }, + new TableOfContentsBlockRequest { TableOfContents = new TableOfContentsBlockRequest.Data() }, + new CalloutBlockRequest + { + Callout = new CalloutBlockRequest.Info + { + RichText = new List + { + new RichTextTextInput { Text = new Text { Content = "Test" } } + } + } + } + } + } + ); + + blocks.Results.Should().HaveCount(4); + } + + [Fact] + public async Task UpdateBlockAsync_UpdatesGivenBlock() + { + var blocks = await Client.Blocks.AppendChildrenAsync( + new BlockAppendChildrenRequest + { + BlockId = _page.Id, + Children = new List + { + new BreadcrumbBlockRequest { Breadcrumb = new BreadcrumbBlockRequest.Data() } + } + } + ); + + var blockId = blocks.Results.First().Id; + await Client.Blocks.UpdateAsync(blockId, new BreadcrumbUpdateBlock()); + + var updatedBlocks = + await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest { BlockId = _page.Id }); + + updatedBlocks.Results.Should().HaveCount(1); + } + + [Fact] + public async Task DeleteAsync_DeleteBlockWithGivenId() + { + var blocks = await Client.Blocks.AppendChildrenAsync( + new BlockAppendChildrenRequest + { + BlockId = _page.Id, + Children = new List + { + new DividerBlockRequest { Divider = new DividerBlockRequest.Data() }, + new TableOfContentsBlockRequest { TableOfContents = new TableOfContentsBlockRequest.Data() } + } + } + ); + + blocks.Results.Should().HaveCount(2); + } + + [Theory] + [MemberData(nameof(BlockData))] + public async Task UpdateAsync_UpdatesGivenBlock( + IBlockObjectRequest block, IUpdateBlock updateBlock, Action assert) + { + var blocks = await Client.Blocks.AppendChildrenAsync( + new BlockAppendChildrenRequest + { + BlockId = _page.Id, + Children = new List { block } + } + ); + + var blockId = blocks.Results.First().Id; + await Client.Blocks.UpdateAsync(blockId, updateBlock); + + var updatedBlocks = + await Client.Blocks.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest { BlockId = _page.Id }); + + updatedBlocks.Results.Should().HaveCount(1); + + var updatedBlock = updatedBlocks.Results.First(); + + assert.Invoke(updatedBlock, Client); + } + + private static IEnumerable BlockData() + { + return new List + { + new object[] + { + new BookmarkBlockRequest + { + Bookmark = new BookmarkBlockRequest.Info + { + Url = "https://developers.notion.com/reference/rich-text", + Caption = new List + { + new RichTextTextInput { Text = new Text { Content = "Notion API" } } + } + } + }, + new BookmarkUpdateBlock + { + Bookmark = new BookmarkUpdateBlock.Info + { + Url = "https://github.com/notion-dotnet/notion-sdk-net", + Caption = new List + { + new RichTextTextInput { Text = new Text { Content = "Github" } } + } + } + }, + new Action((block, _) => + { + var updatedBlock = (BookmarkBlock)block; + Assert.Equal("https://github.com/notion-dotnet/notion-sdk-net", updatedBlock.Bookmark.Url); + Assert.Equal("Github", updatedBlock.Bookmark.Caption.OfType().First().Text.Content); + }) + }, + new object[] + { + new EquationBlockRequest { Equation = new EquationBlockRequest.Info { Expression = "e=mc^3" } }, + new EquationUpdateBlock { Equation = new EquationUpdateBlock.Info { Expression = "e=mc^2" } }, + new Action((block, _) => + { + var updatedBlock = (EquationBlock)block; + Assert.Equal("e=mc^2", updatedBlock.Equation.Expression); + }) + }, + new object[] + { + new DividerBlockRequest { Divider = new DividerBlockRequest.Data() }, new DividerUpdateBlock(), + new Action((block, client) => + { + Assert.NotNull(block); + _ = Assert.IsType(block); + }) + }, + new object[] + { + new AudioBlockRequest + { + Audio = new ExternalFile + { + External = new ExternalFile.Info + { + Url = "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" + } + } + }, + new AudioUpdateBlock + { + Audio = new ExternalFileInput + { + External = new ExternalFileInput.Data + { + Url = "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3" + } + } + }, + new Action((block, _) => + { + block.Should().NotBeNull(); + + block.Should().BeOfType().Subject + .Audio.Should().BeOfType().Subject + .External.Url.Should().Be("https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3"); + }) + }, + new object[] + { + new TableOfContentsBlockRequest { TableOfContents = new TableOfContentsBlockRequest.Data() }, + new TableOfContentsUpdateBlock(), new Action((block, client) => + { + Assert.NotNull(block); + _ = Assert.IsType(block); + }) + }, + new object[] + { + new CalloutBlockRequest + { + Callout = new CalloutBlockRequest.Info + { + RichText = new List + { + new RichTextTextInput { Text = new Text { Content = "Test" } } + } + } + }, + new CalloutUpdateBlock + { + Callout = new CalloutUpdateBlock.Info + { + RichText = new List + { + new RichTextTextInput { Text = new Text { Content = "Test 2" } } + } + } + }, + new Action((block, _) => + { + Assert.NotNull(block); + var calloutBlock = Assert.IsType(block); + + Assert.Equal("Test 2", calloutBlock.Callout.RichText.OfType().First().Text.Content); + }) + }, + new object[] + { + new QuoteBlockRequest + { + Quote = new QuoteBlockRequest.Info + { + RichText = new List + { + new RichTextTextInput { Text = new Text { Content = "Test" } } + } + } + }, + new QuoteUpdateBlock + { + Quote = new QuoteUpdateBlock.Info + { + RichText = new List + { + new RichTextTextInput { Text = new Text { Content = "Test 2" } } + } + } + }, + new Action((block, _) => + { + Assert.NotNull(block); + var quoteBlock = Assert.IsType(block); + + Assert.Equal("Test 2", quoteBlock.Quote.RichText.OfType().First().Text.Content); + }) + }, + new object[] + { + new ImageBlockRequest + { + Image = new ExternalFile + { + External = new ExternalFile.Info + { + Url = "https://zephoria.com/wp-content/uploads/2014/08/online-community.jpg" + } + } + }, + new ImageUpdateBlock + { + Image = new ExternalFileInput + { + External = new ExternalFileInput.Data + { + Url + = "https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg" + } + } + }, + new Action((block, _) => + { + Assert.NotNull(block); + var imageBlock = Assert.IsType(block); + var imageFile = Assert.IsType(imageBlock.Image); + + Assert.Equal("https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg", + imageFile.External.Url); + }) + }, + new object[] + { + new EmbedBlockRequest + { + Embed = new EmbedBlockRequest.Info + { + Url = "https://zephoria.com/wp-content/uploads/2014/08/online-community.jpg" + } + }, + new EmbedUpdateBlock + { + Embed = new EmbedUpdateBlock.Info + { + Url = "https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg" + } + }, + new Action((block, _) => + { + Assert.NotNull(block); + var embedBlock = Assert.IsType(block); + + Assert.Equal("https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg", + embedBlock.Embed.Url); + }) + }, + new object[] + { + new LinkToPageBlockRequest + { + LinkToPage = new PageParent + { + Type = ParentType.PageId, + PageId = "533578e3edf14c0a91a9da6b09bac3ee" + } + }, + new LinkToPageUpdateBlock + { + LinkToPage = new ParentPageInput { PageId = "3c357473a28149a488c010d2b245a589" } + }, + new Action((block, _) => + { + Assert.NotNull(block); + var linkToPageBlock = Assert.IsType(block); + + var pageParent = Assert.IsType(linkToPageBlock.LinkToPage); + + // TODO: Currently the api doesn't allow to update the link_to_page block type + // This will change to updated ID once api start to support + Assert.Equal(Guid.Parse("533578e3edf14c0a91a9da6b09bac3ee"), Guid.Parse(pageParent.PageId)); + }) + }, + new object[] + { + new TableBlockRequest + { + Table = new TableBlockRequest.Info + { + TableWidth = 1, + Children = new[] + { + new TableRowBlockRequest + { + TableRow = new TableRowBlockRequest.Info + { + Cells = new[] + { + new[] { new RichTextText { Text = new Text { Content = "Data" } } } + } + } + } + } + } + }, + new TableUpdateBlock { Table = new TableUpdateBlock.Info { HasColumnHeader = false } }, + new Action((block, client) => + { + var tableBlock = block.Should().NotBeNull().And.BeOfType().Subject; + tableBlock.HasChildren.Should().BeTrue(); + + var children = client.Blocks + .RetrieveChildrenAsync(new BlockRetrieveChildrenRequest { BlockId = tableBlock.Id }) + .GetAwaiter().GetResult(); + + children.Results.Should().ContainSingle() + .Subject.Should().BeOfType() + .Subject.TableRow.Cells.Should().ContainSingle() + .Subject.Should().ContainSingle() + .Subject.Should().BeOfType() + .Subject.Text.Content.Should().Be("Data"); + }) + }, + new object[] + { + new FileBlockRequest { + File = new ExternalFile + { + Name = "Test file", + External = new ExternalFile.Info + { + Url = "https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg" + }, + Caption = new List + { + new RichTextTextInput { Text = new Text { Content = "Test file" } } + } + } + }, + new FileUpdateBlock + { + File = new ExternalFileInput + { + Name = "Test file name", + External = new ExternalFileInput.Data + { + Url = "https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg" + }, + Caption = new List + { + new RichTextTextInput { Text = new Text { Content = "Test file caption" } } + } + } + }, + new Action((block, client) => + { + var fileBlock = block.Should().NotBeNull().And.BeOfType().Subject; + fileBlock.HasChildren.Should().BeFalse(); + + var file = fileBlock.File.Should().NotBeNull().And.BeOfType().Subject; + + // NOTE: The name of the file block, as shown in the Notion UI. Note that the UI may auto-append .pdf or other extensions. + file.Name.Should().Be("Test file name.jpg"); + + file.External.Should().NotBeNull(); + file.External.Url.Should().Be("https://www.iaspaper.net/wp-content/uploads/2017/09/TNEA-Online-Application.jpg"); + file.Caption.Should().NotBeNull().And.ContainSingle() + .Subject.Should().BeOfType().Subject + .Text.Content.Should().Be("Test file caption"); + }) + } + }; + } +} diff --git a/Test/Notion.IntegrationTests/CommentsClientTests.cs b/Test/Notion.IntegrationTests/CommentsClientTests.cs new file mode 100644 index 00000000..fac1fce1 --- /dev/null +++ b/Test/Notion.IntegrationTests/CommentsClientTests.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Notion.Client; +using Xunit; + +namespace Notion.IntegrationTests; + +public class CommentsClientTests : IntegrationTestBase, IAsyncLifetime +{ + private Page _page = null!; + + public async Task InitializeAsync() + { + _page = await Client.Pages.CreateAsync( + PagesCreateParametersBuilder.Create( + new ParentPageInput { PageId = ParentPageId } + ).Build() + ); + } + + public async Task DisposeAsync() + { + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { InTrash = true }); + } + + [Fact] + public async Task ShouldCreatePageComment() + { + // Arrange + var parameters = CreateCommentParameters.CreatePageComment( + new ParentPageInput { PageId = _page.Id }, + new List { new RichTextTextInput { Text = new Text { Content = "This is a comment" } } } + ); + + // Act + var response = await Client.Comments.CreateAsync(parameters); + + // Arrange + + Assert.NotNull(response.Parent); + Assert.NotNull(response.Id); + Assert.NotNull(response.DiscussionId); + + Assert.NotNull(response.RichText); + Assert.Single(response.RichText); + var richText = Assert.IsType(response.RichText.First()); + Assert.Equal("This is a comment", richText.Text.Content); + + var pageParent = Assert.IsType(response.Parent); + Assert.Equal(_page.Id, pageParent.PageId); + } + + [Fact] + public async Task ShouldCreateADiscussionComment() + { + // Arrange + var comment = await Client.Comments.CreateAsync( + CreateCommentParameters.CreatePageComment( + new ParentPageInput { PageId = _page.Id }, + new List + { + new RichTextTextInput { Text = new Text { Content = "This is a comment" } } + } + ) + ); + + // Act + var response = await Client.Comments.CreateAsync( + CreateCommentParameters.CreateDiscussionComment( + comment.DiscussionId, + new List + { + new RichTextTextInput { Text = new Text { Content = "This is a sub comment" } } + } + ) + ); + + // Arrange + Assert.NotNull(response.Parent); + Assert.NotNull(response.Id); + Assert.Equal(comment.DiscussionId, response.DiscussionId); + + Assert.NotNull(response.RichText); + Assert.Single(response.RichText); + var richText = Assert.IsType(response.RichText.First()); + Assert.Equal("This is a sub comment", richText.Text.Content); + + var pageParent = Assert.IsType(response.Parent); + Assert.Equal(_page.Id, pageParent.PageId); + } +} diff --git a/Test/Notion.IntegrationTests/DatabasesClientTests.cs b/Test/Notion.IntegrationTests/DatabasesClientTests.cs new file mode 100644 index 00000000..7abd7b4e --- /dev/null +++ b/Test/Notion.IntegrationTests/DatabasesClientTests.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Notion.Client; +using Xunit; + +namespace Notion.IntegrationTests; + +public class DatabasesClientTests : IntegrationTestBase, IAsyncLifetime +{ + private Page _page = null!; + + public async Task InitializeAsync() + { + _page = await Client.Pages.CreateAsync( + PagesCreateParametersBuilder.Create( + new ParentPageInput { PageId = ParentPageId } + ).Build() + ); + } + + public async Task DisposeAsync() + { + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { InTrash = true }); + } + + [Fact] + public async Task QueryDatabase() + { + // Arrange + var createdDatabase = await CreateDatabaseWithAPageAsync("Test List"); + + // Act + var response = await Client.Databases.QueryAsync(createdDatabase.Id, new DatabasesQueryParameters()); + + // Assert + response.Results.Should().NotBeNull(); + var page = response.Results.Should().ContainSingle().Subject.As(); + + page.Properties["Name"].As() + .Title.Cast().First() + .Text.Content.Should().Be("Test Title"); + } + + [Fact] + public async Task UpdateDatabaseRelationProperties() + { + // Arrange + var createdSourceDatabase = await CreateDatabaseWithAPageAsync("Test Relation Source"); + var createdDestinationDatabase = await CreateDatabaseWithAPageAsync("Test Relation Destination"); + + // Act + var response = await Client.Databases.UpdateAsync(createdDestinationDatabase.Id, + new DatabasesUpdateParameters + { + Properties = new Dictionary + { + { + "Single Relation", + new RelationUpdatePropertySchema + { + Relation = new SinglePropertyRelation + { + DatabaseId = createdSourceDatabase.Id, + SingleProperty = new Dictionary() + } + } + }, + { + "Dual Relation", + new RelationUpdatePropertySchema + { + Relation = new DualPropertyRelation + { + DatabaseId = createdSourceDatabase.Id, + DualProperty = new DualPropertyRelation.Data() + } + } + } + } + }); + + // Assert + response.Properties.Should().NotBeNull(); + + response.Properties.Should().ContainKey("Single Relation"); + var singleRelation = response.Properties["Single Relation"].As().Relation; + singleRelation.Should().BeEquivalentTo( + new SinglePropertyRelation + { + DatabaseId = createdSourceDatabase.Id, + SingleProperty = new Dictionary() + }); + + response.Properties.Should().ContainKey("Dual Relation"); + var dualRelation = response.Properties["Dual Relation"].As().Relation; + dualRelation.DatabaseId.Should().Be(createdSourceDatabase.Id); + dualRelation.Type.Should().Be(RelationType.Dual); + dualRelation.Should().BeOfType(); + } + + private async Task CreateDatabaseWithAPageAsync(string databaseName) + { + var createDbRequest = new DatabasesCreateParameters + { + Title = new List + { + new RichTextTextInput + { + Text = new Text + { + Content = databaseName, + Link = null + } + } + }, + Properties = new Dictionary + { + { "Name", new TitlePropertySchema { Title = new Dictionary() } }, + }, + Parent = new ParentPageInput { PageId = _page.Id } + }; + + var createdDatabase = await Client.Databases.CreateAsync(createDbRequest); + + var pagesCreateParameters = PagesCreateParametersBuilder + .Create(new DatabaseParentInput { DatabaseId = createdDatabase.Id }) + .AddProperty("Name", + new TitlePropertyValue + { + Title = new List + { + new RichTextText { Text = new Text { Content = "Test Title" } } + } + }) + .Build(); + + await Client.Pages.CreateAsync(pagesCreateParameters); + + return createdDatabase; + } + + [Fact] + public async Task Verify_mention_date_property_parsed_properly() + { + // Arrange + var createDbRequest = new DatabasesCreateParameters + { + Title = new List + { + new RichTextTextInput + { + Text = new Text + { + Content = "Test DB", + Link = null + } + }, + new RichTextMentionInput + { + Mention = new MentionInput + { + Date = new Date + { + Start = DateTime.UtcNow, + End = DateTime.UtcNow.AddDays(1) + } + } + } + }, + Properties = new Dictionary + { + { "Name", new TitlePropertySchema { Title = new Dictionary() } }, + }, + Parent = new ParentPageInput { PageId = _page.Id } + }; + + // Act + var createdDatabase = await Client.Databases.CreateAsync(createDbRequest); + + // Assert + var mention = createdDatabase.Title.OfType().First().Mention; + mention.Date.Start.Should().NotBeNull(); + mention.Date.End.Should().NotBeNull(); + } +} diff --git a/Test/Notion.IntegrationTests/IBlocksClientTests.cs b/Test/Notion.IntegrationTests/IBlocksClientTests.cs deleted file mode 100644 index 0db08a7c..00000000 --- a/Test/Notion.IntegrationTests/IBlocksClientTests.cs +++ /dev/null @@ -1,405 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using FluentAssertions; -using Notion.Client; -using Xunit; - -namespace Notion.IntegrationTests -{ - public class IBlocksClientTests - { - private readonly INotionClient _client; - - public IBlocksClientTests() - { - var options = new ClientOptions - { - AuthToken = Environment.GetEnvironmentVariable("NOTION_AUTH_TOKEN") - }; - - _client = NotionClientFactory.Create(options); - } - - [Fact] - public async Task AppendChildrenAsync_AppendsBlocksGivenBlocks() - { - var pageParentId = "3c357473a28149a488c010d2b245a589"; - - var page = await _client.Pages.CreateAsync( - PagesCreateParametersBuilder.Create( - new ParentPageInput() - { - PageId = pageParentId - } - ).Build() - ); - - var blocks = await _client.Blocks.AppendChildrenAsync( - page.Id, - new BlocksAppendChildrenParameters - { - Children = new List() - { - new BreadcrumbBlock - { - Breadcrumb = new BreadcrumbBlock.Data() - }, - new DividerBlock - { - Divider = new DividerBlock.Data() - }, - new TableOfContentsBlock - { - TableOfContents = new TableOfContentsBlock.Data() - }, - new CalloutBlock - { - Callout = new CalloutBlock.Info - { - Text = new List { - new RichTextTextInput - { - Text = new Text - { - Content = "Test" - } - } - } - } - } - } - } - ); - - blocks.Results.Should().HaveCount(4); - - // cleanup - await _client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters - { - Archived = true - }); - } - - [Fact] - public async Task UpdateBlockAsync_UpdatesGivenBlock() - { - var pageParentId = "3c357473a28149a488c010d2b245a589"; - - var page = await _client.Pages.CreateAsync( - PagesCreateParametersBuilder.Create( - new ParentPageInput() - { - PageId = pageParentId - } - ).Build() - ); - - var blocks = await _client.Blocks.AppendChildrenAsync( - page.Id, - new BlocksAppendChildrenParameters - { - Children = new List() - { - new BreadcrumbBlock - { - Breadcrumb = new BreadcrumbBlock.Data() - } - } - } - ); - - var blockId = blocks.Results.First().Id; - await _client.Blocks.UpdateAsync(blockId, new BreadcrumbUpdateBlock()); - - blocks = await _client.Blocks.RetrieveChildrenAsync(page.Id); - blocks.Results.Should().HaveCount(1); - - // cleanup - await _client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters - { - Archived = true - }); - } - - [Fact] - public async Task DeleteAsync_DeleteBlockWithGivenId() - { - var pageParentId = "3c357473a28149a488c010d2b245a589"; - - var page = await _client.Pages.CreateAsync( - PagesCreateParametersBuilder.Create( - new ParentPageInput() - { - PageId = pageParentId - } - ).Build() - ); - - var blocks = await _client.Blocks.AppendChildrenAsync( - page.Id, - new BlocksAppendChildrenParameters - { - Children = new List() - { - new DividerBlock - { - Divider = new DividerBlock.Data() - }, - new TableOfContentsBlock - { - TableOfContents = new TableOfContentsBlock.Data() - }, - } - } - ); - - blocks.Results.Should().HaveCount(2); - - // cleanup - await _client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters - { - Archived = true - }); - } - - [Theory] - [MemberData(nameof(BlockData))] - public async Task UpdateAsync_UpdatesGivenBlock(IBlock block, IUpdateBlock updateBlock, Action assert) - { - var pageParentId = "3c357473a28149a488c010d2b245a589"; - - var page = await _client.Pages.CreateAsync( - PagesCreateParametersBuilder.Create( - new ParentPageInput - { - PageId = pageParentId - } - ).Build() - ); - - var blocks = await _client.Blocks.AppendChildrenAsync( - page.Id, - new BlocksAppendChildrenParameters - { - Children = new List() - { - block - } - } - ); - - var blockId = blocks.Results.First().Id; - await _client.Blocks.UpdateAsync(blockId, updateBlock); - - blocks = await _client.Blocks.RetrieveChildrenAsync(page.Id); - blocks.Results.Should().HaveCount(1); - - var updatedBlock = blocks.Results.First(); - - assert.Invoke(updatedBlock); - - // cleanup - await _client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters - { - Archived = true - }); - } - - private static IEnumerable BlockData() - { - return new List - { - new object[] { - new BookmarkBlock - { - Bookmark = new BookmarkBlock.Info - { - Url = "https://developers.notion.com/reference/rich-text", - Caption = new List - { - new RichTextTextInput - { - Text = new Text - { - Content = "Notion API" - } - } - } - } - }, - new BookmarkUpdateBlock { - Bookmark = new BookmarkUpdateBlock.Data - { - Url = "https://github.com/notion-dotnet/notion-sdk-net", - Caption = new List - { - new RichTextTextInput - { - Text = new Text - { - Content = "Github" - } - } - } - } - }, - new Action((block) => { - var updatedBlock = (BookmarkBlock)block; - Assert.Equal("https://github.com/notion-dotnet/notion-sdk-net", updatedBlock.Bookmark.Url); - Assert.Equal("Github", updatedBlock.Bookmark.Caption.OfType().First().Text.Content); - }) - }, - new object[] { - new EquationBlock - { - Equation = new EquationBlock.Info - { - Expression = "e=mc^3" - } - }, - new EquationUpdateBlock { - Equation = new EquationUpdateBlock.Data - { - Expression = "e=mc^2" - } - }, - new Action((block) => { - var updatedBlock = (EquationBlock)block; - Assert.Equal("e=mc^2", updatedBlock.Equation.Expression); - }) - }, - new object[] { - new DividerBlock { - Divider = new DividerBlock.Data() - }, - new DividerUpdateBlock(), - new Action((block) => { - Assert.NotNull(block); - Assert.IsType(block); - }) - }, - new object[] { - new AudioBlock { - Audio = new ExternalFile { - External = new ExternalFile.Info { - Url = "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" - } - } - }, - new AudioUpdateBlock { - Audio = new ExternalFileInput { - External = new ExternalFileInput.Data { - Url = "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3" - } - } - }, - new Action((block) => { - block.Should().NotBeNull(); - - block.Should().BeOfType().Subject - .Audio.Should().BeOfType().Subject - .External.Url.Should().Be("https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3"); - }) - }, - new object[] - { - new TableOfContentsBlock { - TableOfContents = new TableOfContentsBlock.Data() - }, - new TableOfContentsUpdateBlock(), - new Action((block) => - { - Assert.NotNull(block); - Assert.IsType(block); - }) - }, - new object[] - { - new CalloutBlock - { - Callout = new CalloutBlock.Info - { - Text = new List - { - new RichTextTextInput - { - Text = new Text - { - Content = "Test" - } - } - } - } - }, - new CalloutUpdateBlock() - { - Callout = new CalloutUpdateBlock.Info - { - Text = new List - { - new RichTextTextInput - { - Text = new Text - { - Content = "Test 2" - } - } - } - } - }, - new Action((block) => - { - Assert.NotNull(block); - var calloutBlock = Assert.IsType(block); - - Assert.Equal("Test 2", calloutBlock.Callout.Text.OfType().First().Text.Content); - }) - }, - new object[] - { - new QuoteBlock - { - Quote = new QuoteBlock.Info - { - Text = new List - { - new RichTextTextInput - { - Text = new Text - { - Content = "Test" - } - } - } - } - }, - new QuoteUpdateBlock() - { - Quote = new QuoteUpdateBlock.Info - { - Text = new List - { - new RichTextTextInput - { - Text = new Text - { - Content = "Test 2" - } - } - } - } - }, - new Action((block) => - { - Assert.NotNull(block); - var quoteBlock = Assert.IsType(block); - - Assert.Equal("Test 2", quoteBlock.Quote.Text.OfType().First().Text.Content); - }) - } - }; - } - } -} diff --git a/Test/Notion.IntegrationTests/IPageClientTests.cs b/Test/Notion.IntegrationTests/IPageClientTests.cs deleted file mode 100644 index ae1e317b..00000000 --- a/Test/Notion.IntegrationTests/IPageClientTests.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using FluentAssertions; -using Notion.Client; -using Xunit; - -namespace Notion.IntegrationTests -{ - public class IPageClientTests - { - private readonly INotionClient _client; - - public IPageClientTests() - { - var options = new ClientOptions - { - AuthToken = Environment.GetEnvironmentVariable("NOTION_AUTH_TOKEN") - }; - - _client = NotionClientFactory.Create(options); - } - - [Fact] - public async Task CreateAsync_CreatesANewPage() - { - PagesCreateParameters pagesCreateParameters = PagesCreateParametersBuilder.Create(new DatabaseParentInput - { - DatabaseId = "f86f2262-0751-40f2-8f63-e3f7a3c39fcb" - }) - .AddProperty("Name", new TitlePropertyValue - { - Title = new List - { - new RichTextText - { - Text = new Text - { - Content = "Test Page Title" - } - } - } - }) - .Build(); - - var page = await _client.Pages.CreateAsync(pagesCreateParameters); - - page.Should().NotBeNull(); - page.Parent.Should().BeOfType().Which - .DatabaseId.Should().Be("f86f2262-0751-40f2-8f63-e3f7a3c39fcb"); - - page.Properties.Should().ContainKey("Name"); - page.Properties["Name"].Should().BeOfType().Which - .Title.First().PlainText.Should().Be("Test Page Title"); - - await _client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters - { - Archived = true - }); - } - - [Fact] - public async Task Bug_unable_to_create_page_with_select_property() - { - PagesCreateParameters pagesCreateParameters = PagesCreateParametersBuilder.Create(new DatabaseParentInput - { - DatabaseId = "f86f2262-0751-40f2-8f63-e3f7a3c39fcb" - }) - .AddProperty("Name", new TitlePropertyValue - { - Title = new List - { - new RichTextText - { - Text = new Text - { - Content = "Test Page Title" - } - } - } - }) - .AddProperty("TestSelect", new SelectPropertyValue - { - Select = new SelectOption - { - Id = "dfbfbe65-6f67-4876-9f75-699124334d06" - } - }) - .Build(); - - var page = await _client.Pages.CreateAsync(pagesCreateParameters); - - page.Should().NotBeNull(); - page.Parent.Should().BeOfType().Which - .DatabaseId.Should().Be("f86f2262-0751-40f2-8f63-e3f7a3c39fcb"); - - page.Properties.Should().ContainKey("Name"); - page.Properties["Name"].Should().BeOfType().Which - .Title.First().PlainText.Should().Be("Test Page Title"); - - await _client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters - { - Archived = true - }); - } - - [Fact] - public async Task Test_RetrievePagePropertyItemAsync() - { - PagesCreateParameters pagesCreateParameters = PagesCreateParametersBuilder.Create(new DatabaseParentInput - { - DatabaseId = "f86f2262-0751-40f2-8f63-e3f7a3c39fcb" - }) - .AddProperty("Name", new TitlePropertyValue - { - Title = new List - { - new RichTextText - { - Text = new Text - { - Content = "Test Page Title" - } - } - } - }) - .Build(); - - var page = await _client.Pages.CreateAsync(pagesCreateParameters); - - var property = await _client.Pages.RetrievePagePropertyItem(new RetrievePropertyItemParameters - { - PageId = page.Id, - PropertyId = "title" - }); - - property.Should().NotBeNull(); - property.Should().BeOfType(); - - var listProperty = (ListPropertyItem)property; - - listProperty.Type.Should().BeNull(); - listProperty.Results.Should().SatisfyRespectively(p => - { - p.Should().BeOfType(); - var titleProperty = (TitlePropertyItem)p; - - titleProperty.Title.PlainText.Should().Be("Test Page Title"); - }); - - // cleanup - await _client.Pages.UpdateAsync(page.Id, new PagesUpdateParameters - { - Archived = true - }); - } - } -} diff --git a/Test/Notion.IntegrationTests/IntegrationTestBase.cs b/Test/Notion.IntegrationTests/IntegrationTestBase.cs new file mode 100644 index 00000000..31836c32 --- /dev/null +++ b/Test/Notion.IntegrationTests/IntegrationTestBase.cs @@ -0,0 +1,24 @@ +using System; +using Notion.Client; + +namespace Notion.IntegrationTests; + +public abstract class IntegrationTestBase +{ + protected readonly INotionClient Client; + protected readonly string ParentPageId; + protected readonly string ParentDatabaseId; + + protected IntegrationTestBase() + { + var options = new ClientOptions { AuthToken = Environment.GetEnvironmentVariable("NOTION_AUTH_TOKEN") }; + + Client = NotionClientFactory.Create(options); + + ParentPageId = Environment.GetEnvironmentVariable("NOTION_PARENT_PAGE_ID") + ?? throw new InvalidOperationException("Parent page id is required."); + + ParentDatabaseId = Environment.GetEnvironmentVariable("NOTION_PARENT_DATABASE_ID") + ?? throw new InvalidOperationException("Parent database id is required."); + } +} diff --git a/Test/Notion.IntegrationTests/Notion.IntegrationTests.csproj b/Test/Notion.IntegrationTests/Notion.IntegrationTests.csproj index af02a55a..1284ee3f 100644 --- a/Test/Notion.IntegrationTests/Notion.IntegrationTests.csproj +++ b/Test/Notion.IntegrationTests/Notion.IntegrationTests.csproj @@ -1,7 +1,7 @@ - net5.0 + net6.0 enable false @@ -15,7 +15,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Test/Notion.IntegrationTests/PageClientTests.cs b/Test/Notion.IntegrationTests/PageClientTests.cs new file mode 100644 index 00000000..4938bcfa --- /dev/null +++ b/Test/Notion.IntegrationTests/PageClientTests.cs @@ -0,0 +1,434 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Notion.Client; +using Xunit; + +namespace Notion.IntegrationTests; + +public class PageClientTests : IntegrationTestBase, IAsyncLifetime +{ + private Page _page = null!; + private Database _database = null!; + + public async Task InitializeAsync() + { + // Create a page + _page = await Client.Pages.CreateAsync( + PagesCreateParametersBuilder.Create( + new ParentPageInput { PageId = ParentPageId } + ).Build() + ); + + // Create a database + var createDbRequest = new DatabasesCreateParameters + { + Title = new List + { + new RichTextTextInput + { + Text = new Text + { + Content = "Test List", + Link = null + } + } + }, + Properties = new Dictionary + { + { "Name", new TitlePropertySchema { Title = new Dictionary() } }, + { + "TestSelect", + new SelectPropertySchema + { + Select = new OptionWrapper + { + Options = new List + { + new() { Name = "Red" }, + new() { Name = "Blue" } + } + } + } + }, + { "Number", new NumberPropertySchema { Number = new Number { Format = "number" } } } + }, + Parent = new ParentPageInput { PageId = _page.Id } + }; + + _database = await Client.Databases.CreateAsync(createDbRequest); + } + + public async Task DisposeAsync() + { + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { InTrash = true }); + } + + [Fact] + public async Task CreateAsync_CreatesANewPage() + { + var pagesCreateParameters = PagesCreateParametersBuilder + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) + .AddProperty("Name", + new TitlePropertyValue + { + Title = new List + { + new RichTextText { Text = new Text { Content = "Test Page Title" } } + } + }) + .Build(); + + var page = await Client.Pages.CreateAsync(pagesCreateParameters); + + page.Should().NotBeNull(); + + page.Parent.Should().BeOfType().Which + .DatabaseId.Should().Be(_database.Id); + + page.Properties.Should().ContainKey("Name"); + var pageProperty = page.Properties["Name"].Should().BeOfType().Subject; + + var titleProperty + = (ListPropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = pageProperty.Id + }); + + titleProperty.Results.First().As().Title.PlainText.Should().Be("Test Page Title"); + } + + [Fact] + public async Task Bug_unable_to_create_page_with_select_property() + { + // Arrange + var pagesCreateParameters = PagesCreateParametersBuilder + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) + .AddProperty("Name", + new TitlePropertyValue + { + Title = new List + { + new RichTextText { Text = new Text { Content = "Test Page Title" } } + } + }) + .AddProperty("TestSelect", + new SelectPropertyValue { Select = new SelectOption { Name = "Blue" } }) + .Build(); + + // Act + var page = await Client.Pages.CreateAsync(pagesCreateParameters); + + // Asserts + page.Should().NotBeNull(); + + page.Parent.Should().BeOfType().Which + .DatabaseId.Should().Be(_database.Id); + + page.Properties.Should().ContainKey("Name"); + var titlePropertyValue = page.Properties["Name"].Should().BeOfType().Subject; + titlePropertyValue.Title.First().PlainText.Should().Be("Test Page Title"); + + page.Properties.Should().ContainKey("TestSelect"); + var selectPropertyValue = page.Properties["TestSelect"].Should().BeOfType().Subject; + selectPropertyValue.Select.Name.Should().Be("Blue"); + } + + [Fact] + public async Task Test_RetrievePagePropertyItemAsync() + { + // Arrange + var pagesCreateParameters = PagesCreateParametersBuilder + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) + .AddProperty("Name", + new TitlePropertyValue + { + Title = new List + { + new RichTextText { Text = new Text { Content = "Test Page Title" } } + } + }) + .Build(); + + var page = await Client.Pages.CreateAsync(pagesCreateParameters); + + // Act + var property = await Client.Pages.RetrievePagePropertyItemAsync(new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = "title" + }); + + // Assert + property.Should().NotBeNull(); + property.Should().BeOfType(); + + var listProperty = (ListPropertyItem)property; + + listProperty.Type.Should().NotBeNull(); + + listProperty.Results.Should().SatisfyRespectively(p => + { + p.Should().BeOfType(); + var titleProperty = (TitlePropertyItem)p; + + titleProperty.Title.PlainText.Should().Be("Test Page Title"); + }); + } + + [Fact] + public async Task Test_UpdatePageProperty_with_date_as_null() + { + // Arrange + + // Add property Date property to database + const string DatePropertyName = "Test Date Property"; + + var updateDatabaseParameters = new DatabasesUpdateParameters + { + Properties = new Dictionary + { + { "Name", new TitleUpdatePropertySchema { Title = new Dictionary() } }, + { + "Test Date Property", + new DateUpdatePropertySchema { Date = new Dictionary() } + } + } + }; + + // Create a page with the property having a date + var pagesCreateParameters = PagesCreateParametersBuilder + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) + .AddProperty("Name", + new TitlePropertyValue + { + Title = new List + { + new RichTextText { Text = new Text { Content = "Test Page Title" } } + } + }) + .AddProperty(DatePropertyName, + new DatePropertyValue + { + Date = new Date + { + Start = DateTimeOffset.Parse("2024-06-26T00:00:00.000+01:00"), + End = DateTimeOffset.Parse("2025-12-08").Date + } + }) + .Build(); + + await Client.Databases.UpdateAsync(_database.Id, updateDatabaseParameters); + + var page = await Client.Pages.CreateAsync(pagesCreateParameters); + + // Act + var setDate = (DatePropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = page.Properties[DatePropertyName].Id + } + ); + + // Assert + setDate?.Date?.Start.Should().Be(DateTimeOffset.Parse("2024-06-26T00:00:00.000+01:00")); + setDate?.Date?.End.Should().Be(DateTimeOffset.Parse("2025-12-08T00:00:00.000+01:00")); + + var pageUpdateParameters = new PagesUpdateParameters + { + Properties = new Dictionary + { + { DatePropertyName, new DatePropertyValue { Date = null } } + } + }; + + var updatedPage = await Client.Pages.UpdateAsync(page.Id, pageUpdateParameters); + + var verifyDate = (DatePropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = updatedPage.Properties[DatePropertyName].Id + } + ); + + verifyDate?.Date.Should().BeNull(); + } + + [Fact] + public async Task Bug_Unable_To_Parse_NumberPropertyItem() + { + // Arrange + var pagesCreateParameters = PagesCreateParametersBuilder + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) + .AddProperty("Name", + new TitlePropertyValue + { + Title = new List + { + new RichTextText { Text = new Text { Content = "Test Page Title" } } + } + }) + .AddProperty("Number", new NumberPropertyValue { Number = 200.00 }).Build(); + + // Act + var page = await Client.Pages.CreateAsync(pagesCreateParameters); + + // Assert + Assert.NotNull(page); + var pageParent = Assert.IsType(page.Parent); + Assert.Equal(_database.Id, pageParent.DatabaseId); + + var titleProperty = (ListPropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = page.Properties["Name"].Id + }); + + Assert.Equal("Test Page Title", titleProperty.Results.First().As().Title.PlainText); + + var numberProperty = (NumberPropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = page.Properties["Number"].Id + }); + + Assert.Equal(200.00, numberProperty.Number); + } + + [Fact] + public async Task Bug_exception_when_attempting_to_set_select_property_to_nothing() + { + // Arrange + var databaseCreateRequest = new DatabasesCreateParameters + { + Title = + new List { new RichTextTextInput { Text = new Text { Content = "Test Database" } } }, + Parent = new ParentPageInput() { PageId = _page.Id }, + Properties = new Dictionary + { + { "title", new TitlePropertySchema { Title = new Dictionary() } }, + { + "Colors1", + new SelectPropertySchema + { + Select = new OptionWrapper + { + Options = new List + { + new() { Name = "Red" }, + new() { Name = "Green" }, + new() { Name = "Blue" } + } + } + } + }, + { + "Colors2", + new SelectPropertySchema + { + Select = new OptionWrapper + { + Options = new List + { + new() { Name = "Red" }, + new() { Name = "Green" }, + new() { Name = "Blue" } + } + } + } + }, + } + }; + + var database = await Client.Databases.CreateAsync(databaseCreateRequest); + + var pagesCreateParameters = PagesCreateParametersBuilder + .Create(new DatabaseParentInput { DatabaseId = database.Id }) + .AddProperty("title", + new TitlePropertyValue + { + Title = new List + { + new RichTextTextInput { Text = new Text { Content = "Test" } } + } + }) + .AddProperty("Colors1", new SelectPropertyValue { Select = new SelectOption { Name = "Red" } }) + .AddProperty("Colors2", new SelectPropertyValue { Select = new SelectOption { Name = "Green" } }) + .Build(); + + // Act + var page = await Client.Pages.CreateAsync(pagesCreateParameters); + + var updatePageRequest = new PagesUpdateParameters + { + Properties = new Dictionary + { + { "Colors1", new SelectPropertyValue { Select = new SelectOption { Name = "Blue" } } }, + { "Colors2", new SelectPropertyValue { Select = null } } + } + }; + + var updatedPage = await Client.Pages.UpdateAsync(page.Id, updatePageRequest); + + // Assert + page.Properties["Colors1"].As().Select.Name.Should().Be("Red"); + page.Properties["Colors2"].As().Select.Name.Should().Be("Green"); + + updatedPage.Properties["Colors1"].As().Select.Name.Should().Be("Blue"); + updatedPage.Properties["Colors2"].As().Select.Should().BeNull(); + } + + [Fact] + public async Task Verify_date_property_is_parsed_correctly_in_mention_object() + { + var pageRequest = PagesCreateParametersBuilder + .Create(new DatabaseParentInput { DatabaseId = _database.Id }) + .AddProperty("Name", + new TitlePropertyValue + { + Title = new List + { + new RichTextMention() + { + Mention = new Mention() + { + Date = new Date() + { + Start = DateTime.UtcNow + } + } + } + } + }) + .Build(); + + var page = await Client.Pages.CreateAsync(pageRequest); + + page.Should().NotBeNull(); + + page.Parent.Should().BeOfType().Which + .DatabaseId.Should().Be(_database.Id); + + page.Properties.Should().ContainKey("Name"); + var pageProperty = page.Properties["Name"].Should().BeOfType().Subject; + + var titleProperty = (ListPropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = pageProperty.Id + }); + + var mention = titleProperty.Results.First().As().Title.As().Mention; + mention.Date.Start.Should().NotBeNull(); + mention.Date.End.Should().BeNull(); + } +} diff --git a/Test/Notion.IntegrationTests/PageWithPageParentTests.cs b/Test/Notion.IntegrationTests/PageWithPageParentTests.cs new file mode 100644 index 00000000..3c6d3c6c --- /dev/null +++ b/Test/Notion.IntegrationTests/PageWithPageParentTests.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Notion.Client; +using Xunit; + +namespace Notion.IntegrationTests; + +public class PageWithPageParentTests : IntegrationTestBase, IAsyncLifetime +{ + private Page _page = null!; + + public async Task InitializeAsync() + { + var pagesCreateParameters = PagesCreateParametersBuilder + .Create(new ParentPageInput() { PageId = ParentPageId }) + .AddProperty("title", + new TitlePropertyValue + { + Title = new List + { + new RichTextTextInput { Text = new Text { Content = "Test Page Title" } } + } + }).Build(); + + _page = await Client.Pages.CreateAsync(pagesCreateParameters); + } + + public async Task DisposeAsync() + { + await Client.Pages.UpdateAsync(_page.Id, new PagesUpdateParameters { InTrash = true }); + } + + [Fact] + public async Task Update_Title_Of_Page() + { + // Arrange + var updatePage = new PagesUpdateParameters() + { + Properties = new Dictionary + { + { + "title", + new TitlePropertyValue() + { + Title = new List + { + new RichTextText { Text = new Text() { Content = "Page Title Updated" } } + } + } + } + } + }; + + // Act + var updatedPage = await Client.Pages.UpdateAsync(_page.Id, updatePage); + + // Assert + var titleProperty = (ListPropertyItem)await Client.Pages.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = updatedPage.Id, + PropertyId = updatedPage.Properties["title"].Id + } + ); + + titleProperty.Results.First().As().Title.PlainText.Should().Be("Page Title Updated"); + } +} diff --git a/Test/Notion.UnitTests/ApiTestBase.cs b/Test/Notion.UnitTests/ApiTestBase.cs index e593a47c..51dfd7fc 100644 --- a/Test/Notion.UnitTests/ApiTestBase.cs +++ b/Test/Notion.UnitTests/ApiTestBase.cs @@ -1,70 +1,58 @@ using System; using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; using Notion.Client; using WireMock.Matchers; using WireMock.RequestBuilders; using WireMock.Server; -namespace Notion.UnitTests +namespace Notion.UnitTests; + +public class ApiTestBase : IDisposable { - public class ApiTestBase : IDisposable + protected readonly ClientOptions ClientOptions; + protected readonly WireMockServer Server; + + protected ApiTestBase() { - protected readonly WireMockServer Server; + Server = WireMockServer.Start(); - protected static readonly JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings() + ClientOptions = new ClientOptions { - Formatting = Newtonsoft.Json.Formatting.Indented, - ContractResolver = new DefaultContractResolver - { - NamingStrategy = new CamelCaseNamingStrategy() - } + BaseUrl = Server.Urls.First(), + AuthToken = "" }; + } - protected readonly ClientOptions ClientOptions; - - protected ApiTestBase() - { - Server = WireMockServer.Start(); - ClientOptions = new ClientOptions() - { - BaseUrl = Server.Urls.First(), - AuthToken = "" - }; - } - - public void Dispose() - { - Server.Stop(); - Server.Dispose(); - } + public void Dispose() + { + Server.Stop(); + Server.Dispose(); + } - protected IRequestBuilder CreateGetRequestBuilder(string path) - { - return Request.Create() - .WithPath(path) - .UsingGet() - .WithHeader("Authorization", $"Bearer {ClientOptions.AuthToken}", MatchBehaviour.AcceptOnMatch) - .WithHeader("Notion-Version", Constants.DEFAULT_NOTION_VERSION, MatchBehaviour.AcceptOnMatch); - } + protected IRequestBuilder CreateGetRequestBuilder(string path) + { + return Request.Create() + .WithPath(path) + .UsingGet() + .WithHeader("Authorization", $"Bearer {ClientOptions.AuthToken}", MatchBehaviour.AcceptOnMatch) + .WithHeader("Notion-Version", Constants.DefaultNotionVersion, MatchBehaviour.AcceptOnMatch); + } - protected IRequestBuilder CreatePostRequestBuilder(string path) - { - return Request.Create() - .WithPath(path) - .UsingPost() - .WithHeader("Authorization", $"Bearer {ClientOptions.AuthToken}", MatchBehaviour.AcceptOnMatch) - .WithHeader("Notion-Version", Constants.DEFAULT_NOTION_VERSION, MatchBehaviour.AcceptOnMatch); - } + protected IRequestBuilder CreatePostRequestBuilder(string path) + { + return Request.Create() + .WithPath(path) + .UsingPost() + .WithHeader("Authorization", $"Bearer {ClientOptions.AuthToken}", MatchBehaviour.AcceptOnMatch) + .WithHeader("Notion-Version", Constants.DefaultNotionVersion, MatchBehaviour.AcceptOnMatch); + } - protected IRequestBuilder CreatePatchRequestBuilder(string path) - { - return Request.Create() - .WithPath(path) - .UsingPatch() - .WithHeader("Authorization", $"Bearer {ClientOptions.AuthToken}", MatchBehaviour.AcceptOnMatch) - .WithHeader("Notion-Version", Constants.DEFAULT_NOTION_VERSION, MatchBehaviour.AcceptOnMatch); - } + protected IRequestBuilder CreatePatchRequestBuilder(string path) + { + return Request.Create() + .WithPath(path) + .UsingPatch() + .WithHeader("Authorization", $"Bearer {ClientOptions.AuthToken}", MatchBehaviour.AcceptOnMatch) + .WithHeader("Notion-Version", Constants.DefaultNotionVersion, MatchBehaviour.AcceptOnMatch); } } diff --git a/Test/Notion.UnitTests/BlocksClientTests.cs b/Test/Notion.UnitTests/BlocksClientTests.cs index 3e1a1b53..e2cc8603 100644 --- a/Test/Notion.UnitTests/BlocksClientTests.cs +++ b/Test/Notion.UnitTests/BlocksClientTests.cs @@ -7,191 +7,189 @@ using WireMock.ResponseBuilders; using Xunit; -namespace Notion.UnitTests +namespace Notion.UnitTests; + +public class BlocksClientTests : ApiTestBase { - public class BlocksClientTests : ApiTestBase - { - private readonly IBlocksClient _client; + private readonly IBlocksClient _client; - public BlocksClientTests() - { - _client = new BlocksClient(new RestClient(ClientOptions)); - } + public BlocksClientTests() + { + _client = new BlocksClient(new RestClient(ClientOptions)); + } - [Fact] - public async Task RetrieveBlockChildren() - { - // Arrange - string blockId = "3c357473-a281-49a4-88c0-10d2b245a589"; - var path = ApiEndpoints.BlocksApiUrls.RetrieveChildren(blockId); - var jsonData = await File.ReadAllTextAsync("data/blocks/RetrieveBlockChildrenResponse.json"); + [Fact] + public async Task RetrieveBlockChildren() + { + // Arrange + var blockId = "3c357473-a281-49a4-88c0-10d2b245a589"; + var path = ApiEndpoints.BlocksApiUrls.RetrieveChildren(blockId); + var jsonData = await File.ReadAllTextAsync("data/blocks/RetrieveBlockChildrenResponse.json"); - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - // Act - var childrenResult = await _client.RetrieveChildrenAsync(blockId, new BlocksRetrieveChildrenParameters()); + // Act + var childrenResult = await _client.RetrieveChildrenAsync(new BlockRetrieveChildrenRequest + { + BlockId = blockId + }); - // Assert - var children = childrenResult.Results; - children.Should().HaveCount(7); - } + // Assert + var children = childrenResult.Results; + children.Should().HaveCount(9); + } - [Fact] - public async Task AppendBlockChildren() - { - // Arrange - string blockId = "7face6fd-3ef4-4b38-b1dc-c5044988eec0"; - var path = ApiEndpoints.BlocksApiUrls.AppendChildren(blockId); + [Fact] + public async Task AppendBlockChildren() + { + // Arrange + var blockId = "7face6fd-3ef4-4b38-b1dc-c5044988eec0"; + var path = ApiEndpoints.BlocksApiUrls.AppendChildren(blockId); - var jsonData = await File.ReadAllTextAsync("data/blocks/AppendBlockChildrenResponse.json"); + var jsonData = await File.ReadAllTextAsync("data/blocks/AppendBlockChildrenResponse.json"); - Server.Given(CreatePatchRequestBuilder(path)) - .RespondWith( + Server.Given(CreatePatchRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var parameters = new BlocksAppendChildrenParameters() + var request = new BlockAppendChildrenRequest + { + BlockId = blockId, + Children = new List { - Children = new List + new HeadingTwoBlockRequest { - new HeadingTwoBlock() + Heading_2 = new HeadingTwoBlockRequest.Info { - Heading_2 = new HeadingTwoBlock.Info + RichText = new List { - Text = new List - { - new RichTextText - { - Text = new Text - { - Content = "Lacinato kale" - } - } - } + new RichTextText { Text = new Text { Content = "Lacinato kale" } } } - }, - new ParagraphBlock() + } + }, + new ParagraphBlockRequest + { + Paragraph = new ParagraphBlockRequest.Info { - Paragraph = new ParagraphBlock.Info + RichText = new List { - Text = new List + new RichTextText { - new RichTextText + Text = new Text { - Text = new Text + Content + = "Lacinato kale is a variety of kale with a long tradition in Italian cuisine, especially that of Tuscany. It is also known as Tuscan kale, Italian kale, dinosaur kale, kale, flat back kale, palm tree kale, or black Tuscan palm.", + Link = new Link { - Content = "Lacinato kale is a variety of kale with a long tradition in Italian cuisine, especially that of Tuscany. It is also known as Tuscan kale, Italian kale, dinosaur kale, kale, flat back kale, palm tree kale, or black Tuscan palm.", - Link = new Link - { - Url = "https://en.wikipedia.org/wiki/Lacinato_kale" - } + Url + = "https://en.wikipedia.org/wiki/Lacinato_kale" } } } } } } - }; + } + }; - // Act - var blocksResult = await _client.AppendChildrenAsync(blockId, parameters); + // Act + var blocksResult = await _client.AppendChildrenAsync(request); - // Assert - var blocks = blocksResult.Results; - blocks.Should().SatisfyRespectively( - block => - { - block.Type.Should().Be(BlockType.Heading_2); - var headingBlock = (HeadingTwoBlock)block; - var text = headingBlock.Heading_2.Text.OfType().FirstOrDefault(); - text.Text.Content.Should().Be("Lacinato kale"); - }, - block => - { - block.Type.Should().Be(BlockType.Paragraph); - var paragraphBlock = (ParagraphBlock)block; - var text = paragraphBlock.Paragraph.Text.OfType().LastOrDefault(); - text.Text.Content.Should().Be("Lacinato kale is a variety of kale with a long tradition in Italian cuisine, especially that of Tuscany. It is also known as Tuscan kale, Italian kale, dinosaur kale, kale, flat back kale, palm tree kale, or black Tuscan palm."); - text.Text.Link.Url.Should().Be("https://en.wikipedia.org/wiki/Lacinato_kale"); - } - ); - } + // Assert + var blocks = blocksResult.Results; - [Fact] - public async Task RetrieveAsync() - { - string blockId = "9bc30ad4-9373-46a5-84ab-0a7845ee52e6"; - var path = ApiEndpoints.BlocksApiUrls.Retrieve(blockId); - var jsonData = await File.ReadAllTextAsync("data/blocks/RetrieveBlockResponse.json"); + blocks.Should().SatisfyRespectively( + block => + { + block.Type.Should().Be(BlockType.Heading_2); + var headingBlock = (HeadingTwoBlock)block; + var text = headingBlock.Heading_2.RichText.OfType().First(); + text.Text.Content.Should().Be("Lacinato kale"); + }, + block => + { + block.Type.Should().Be(BlockType.Paragraph); + var paragraphBlock = (ParagraphBlock)block; + var text = paragraphBlock.Paragraph.RichText.OfType().Last(); + + text.Text.Content.Should().Be( + "Lacinato kale is a variety of kale with a long tradition in Italian cuisine, especially that of Tuscany. It is also known as Tuscan kale, Italian kale, dinosaur kale, kale, flat back kale, palm tree kale, or black Tuscan palm."); + + text.Text.Link.Url.Should().Be("https://en.wikipedia.org/wiki/Lacinato_kale"); + } + ); + } + + [Fact] + public async Task RetrieveAsync() + { + var blockId = "9bc30ad4-9373-46a5-84ab-0a7845ee52e6"; + var path = ApiEndpoints.BlocksApiUrls.Retrieve(blockId); + var jsonData = await File.ReadAllTextAsync("data/blocks/RetrieveBlockResponse.json"); - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var block = await _client.RetrieveAsync(blockId); + var block = await _client.RetrieveAsync(blockId); - block.Id.Should().Be(blockId); - block.HasChildren.Should().BeFalse(); - block.Type.Should().Be(BlockType.ToDo); + block.Id.Should().Be(blockId); + block.HasChildren.Should().BeFalse(); + block.Type.Should().Be(BlockType.ToDo); - var todoBlock = ((ToDoBlock)block); - todoBlock.ToDo.Text.Should().ContainSingle(); - todoBlock.ToDo.Text.First().Should().BeAssignableTo(); - ((RichTextText)todoBlock.ToDo.Text.First()).Text.Content.Should().Be("Lacinato kale"); - } + var todoBlock = (ToDoBlock)block; + todoBlock.ToDo.RichText.Should().ContainSingle(); + todoBlock.ToDo.RichText.First().Should().BeAssignableTo(); + ((RichTextText)todoBlock.ToDo.RichText.First()).Text.Content.Should().Be("Lacinato kale"); + } - [Fact] - public async Task UpdateAsync() - { - string blockId = "9bc30ad4-9373-46a5-84ab-0a7845ee52e6"; - var path = ApiEndpoints.BlocksApiUrls.Update(blockId); - var jsonData = await File.ReadAllTextAsync("data/blocks/UpdateBlockResponse.json"); + [Fact] + public async Task UpdateAsync() + { + var blockId = "9bc30ad4-9373-46a5-84ab-0a7845ee52e6"; + var path = ApiEndpoints.BlocksApiUrls.Update(blockId); + var jsonData = await File.ReadAllTextAsync("data/blocks/UpdateBlockResponse.json"); - Server.Given(CreatePatchRequestBuilder(path)) - .RespondWith( + Server.Given(CreatePatchRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var updateBlock = new ToDoUpdateBlock + var updateBlock = new ToDoUpdateBlock + { + ToDo = new ToDoUpdateBlock.Info { - ToDo = new ToDoUpdateBlock.Info + RichText = new List { - Text = new List() - { - new RichTextTextInput - { - Text = new Text - { - Content = "Lacinato kale" - }, - } - }, - IsChecked = true - } - }; + new RichTextTextInput { Text = new Text { Content = "Lacinato kale" } } + }, + IsChecked = true + } + }; - var block = await _client.UpdateAsync(blockId, updateBlock); + var block = await _client.UpdateAsync(blockId, updateBlock); - block.Id.Should().Be(blockId); - block.HasChildren.Should().BeFalse(); - block.Type.Should().Be(BlockType.ToDo); + block.Id.Should().Be(blockId); + block.HasChildren.Should().BeFalse(); + block.InTrash.Should().BeFalse(); + block.Type.Should().Be(BlockType.ToDo); - var todoBlock = ((ToDoBlock)block); - todoBlock.ToDo.Text.Should().ContainSingle(); - todoBlock.ToDo.Text.First().Should().BeAssignableTo(); - ((RichTextText)todoBlock.ToDo.Text.First()).Text.Content.Should().Be("Lacinato kale"); - } + var todoBlock = (ToDoBlock)block; + todoBlock.ToDo.RichText.Should().ContainSingle(); + todoBlock.ToDo.RichText.First().Should().BeAssignableTo(); + ((RichTextText)todoBlock.ToDo.RichText.First()).Text.Content.Should().Be("Lacinato kale"); } } diff --git a/Test/Notion.UnitTests/DatabasesClientTests.cs b/Test/Notion.UnitTests/DatabasesClientTests.cs index d6a440a0..30deae75 100644 --- a/Test/Notion.UnitTests/DatabasesClientTests.cs +++ b/Test/Notion.UnitTests/DatabasesClientTests.cs @@ -7,188 +7,194 @@ using WireMock.ResponseBuilders; using Xunit; -namespace Notion.UnitTests +namespace Notion.UnitTests; + +public class DatabasesClientTests : ApiTestBase { - public class DatabasesClientTests : ApiTestBase - { - private readonly IDatabasesClient _client; + private readonly IDatabasesClient _client; + private readonly IPagesClient _pagesClient; - public DatabasesClientTests() - { - _client = new DatabasesClient(new RestClient(ClientOptions)); - } + public DatabasesClientTests() + { + _client = new DatabasesClient(new RestClient(ClientOptions)); + _pagesClient = new PagesClient(new RestClient(ClientOptions)); + } - [Fact] - [Obsolete] - public async Task ListDatabasesAsync() - { - var path = ApiEndpoints.DatabasesApiUrls.List(); - var jsonData = await File.ReadAllTextAsync("data/databases/DatabasesListResponse.json"); + [Fact] + public async Task QueryAsync() + { + var databaseId = "f0212efc-caf6-4afc-87f6-1c06f1dfc8a1"; + var path = ApiEndpoints.DatabasesApiUrls.Query(databaseId); + var jsonData = await File.ReadAllTextAsync("data/databases/DatabasesQueryResponse.json"); - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( + Server.Given(CreatePostRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var databases = await _client.ListAsync(); - - databases.Results.Should().HaveCount(3); - - foreach (var database in databases.Results) + var databasesQueryParams = new DatabasesQueryParameters + { + Filter = new CompoundFilter + { + Or = new List + { + new CheckboxFilter( + "In stock", + true + ), + new NumberFilter( + "Cost of next trip", + greaterThanOrEqualTo: 2 + ) + } + }, + Sorts = new List { - database.Parent.Should().BeAssignableTo(); - foreach (var property in database.Properties) + new() { - property.Key.Should().Be(property.Value.Name); + Property = "Last ordered", + Direction = Direction.Ascending } } - } + }; + + var pagesPaginatedList = await _client.QueryAsync(databaseId, databasesQueryParams); + + pagesPaginatedList.Results.Should().ContainSingle(); - [Fact] - public async Task QueryAsync() + foreach (var iWikiDatabase in pagesPaginatedList.Results) { - var databaseId = "f0212efc-caf6-4afc-87f6-1c06f1dfc8a1"; - var path = ApiEndpoints.DatabasesApiUrls.Query(databaseId); - var jsonData = await File.ReadAllTextAsync("data/databases/DatabasesQueryResponse.json"); + var page = (Page)iWikiDatabase; + page.Parent.Should().BeAssignableTo(); + page.Object.Should().Be(ObjectType.Page); + } + } - Server.Given(CreatePostRequestBuilder(path)) - .RespondWith( + [Fact] + public async Task RetrieveDatabaseAsync() + { + var databaseId = "f0212efc-caf6-4afc-87f6-1c06f1dfc8a1"; + var path = ApiEndpoints.DatabasesApiUrls.Retrieve(databaseId); + var jsonData = await File.ReadAllTextAsync("data/databases/DatabaseRetrieveResponse.json"); + + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var databasesQueryParams = new DatabasesQueryParameters - { - Filter = new CompoundFilter - { - Or = new List { - new CheckboxFilter( - "In stock", - true - ), - new NumberFilter( - "Cost of next trip", - greaterThanOrEqualTo: 2 - ) - }, - }, - Sorts = new List - { - new Sort - { - Property = "Last ordered", - Direction = Direction.Ascending - } - } - }; + var database = await _client.RetrieveAsync(databaseId); - var pagesPaginatedList = await _client.QueryAsync(databaseId, databasesQueryParams); + database.Parent.Type.Should().Be(ParentType.PageId); + database.Parent.Should().BeOfType(); + ((PageParent)database.Parent).PageId.Should().Be("649089db-8984-4051-98fb-a03593b852d8"); - pagesPaginatedList.Results.Should().ContainSingle(); - - foreach (var page in pagesPaginatedList.Results) - { - page.Parent.Should().BeAssignableTo(); - page.Object.Should().Be(ObjectType.Page); - } + foreach (var property in database.Properties) + { + property.Key.Should().Be(property.Value.Name); } - [Fact] - public async Task RetrieveDatabaseAsync() - { - var databaseId = "f0212efc-caf6-4afc-87f6-1c06f1dfc8a1"; - var path = ApiEndpoints.DatabasesApiUrls.Retrieve(databaseId); - var jsonData = await File.ReadAllTextAsync("data/databases/DatabaseRetrieveResponse.json"); + HelperAsserts.IPageIconAsserts(database.Icon); + HelperAsserts.FileObjectAsserts(database.Cover); + } - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( + [Fact] + public async Task DatabasePropertyObjectContainNameProperty() + { + var databaseId = "f0212efc-caf6-4afc-87f6-1c06f1dfc8a1"; + var path = ApiEndpoints.DatabasesApiUrls.Retrieve(databaseId); + var jsonData = await File.ReadAllTextAsync("data/databases/DatabasePropertyObjectContainNameProperty.json"); + + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var database = await _client.RetrieveAsync(databaseId); + var database = await _client.RetrieveAsync(databaseId); - database.Parent.Type.Should().Be(ParentType.PageId); - database.Parent.Should().BeOfType(); - ((PageParent)database.Parent).PageId.Should().Be("649089db-8984-4051-98fb-a03593b852d8"); - foreach (var property in database.Properties) - { - property.Key.Should().Be(property.Value.Name); - } - - HelperAsserts.IPageIconAsserts(database.Icon); - HelperAsserts.FileObjectAsserts(database.Cover); + foreach (var property in database.Properties) + { + property.Key.Should().Be(property.Value.Name); } + } - [Fact] - public async Task DatabasePropertyObjectContainNameProperty() - { - var databaseId = "f0212efc-caf6-4afc-87f6-1c06f1dfc8a1"; - var path = ApiEndpoints.DatabasesApiUrls.Retrieve(databaseId); - var jsonData = await File.ReadAllTextAsync("data/databases/DatabasePropertyObjectContainNameProperty.json"); + [Fact] + public async Task DatabasePropertyObjectContainRelationProperty() + { + var databaseId = "f0212efc-caf6-4afc-87f6-1c06f1dfc8a1"; + var path = ApiEndpoints.DatabasesApiUrls.Retrieve(databaseId); + var jsonData = await File.ReadAllTextAsync("data/databases/DatabasePropertyObjectContainRelation.json"); - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var database = await _client.RetrieveAsync(databaseId); + var database = await _client.RetrieveAsync(databaseId); - foreach (var property in database.Properties) + database.Properties.Should().ContainKey("Property").WhichValue.Should().BeEquivalentTo( + new RelationProperty { - property.Key.Should().Be(property.Value.Name); - } - } + Id = "zDGa", + Name = "Property", + Relation = new DualPropertyRelation + { + DatabaseId = "f86f2262-0751-40f2-8f63-e3f7a3c39fcb", + DualProperty = new DualPropertyRelation.Data + { + SyncedPropertyName = "Related to sample table (Property)", + SyncedPropertyId = "VQ}{" + } + } + }); + } - [Fact] - public async Task DatabasePropertyObjectContainParentProperty() - { - var databaseId = "f0212efc-caf6-4afc-87f6-1c06f1dfc8a1"; - var path = ApiEndpoints.DatabasesApiUrls.Retrieve(databaseId); - var jsonData = await File.ReadAllTextAsync("data/databases/DatabasePropertyObjectContainParentProperty.json"); + [Fact] + public async Task DatabasePropertyObjectContainParentProperty() + { + var databaseId = "f0212efc-caf6-4afc-87f6-1c06f1dfc8a1"; + var path = ApiEndpoints.DatabasesApiUrls.Retrieve(databaseId); + var jsonData = await File.ReadAllTextAsync("data/databases/DatabasePropertyObjectContainParentProperty.json"); - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var database = await _client.RetrieveAsync(databaseId); + var database = await _client.RetrieveAsync(databaseId); - database.Parent.Type.Should().Be(ParentType.PageId); - database.Parent.Should().BeOfType(); - ((PageParent)database.Parent).PageId.Should().Be("649089db-8984-4051-98fb-a03593b852d8"); - } + database.Parent.Type.Should().Be(ParentType.PageId); + database.Parent.Should().BeOfType(); + ((PageParent)database.Parent).PageId.Should().Be("649089db-8984-4051-98fb-a03593b852d8"); + } - [Fact] - public async Task CreateDatabaseAsync() - { - var pageId = "533578e3-edf1-4c0a-91a9-da6b09bac3ee"; - var path = ApiEndpoints.DatabasesApiUrls.Create; - var jsonData = await File.ReadAllTextAsync("data/databases/CreateDatabaseResponse.json"); + [Fact] + public async Task CreateDatabaseAsync() + { + var pageId = "533578e3-edf1-4c0a-91a9-da6b09bac3ee"; + var path = ApiEndpoints.DatabasesApiUrls.Create; + var jsonData = await File.ReadAllTextAsync("data/databases/CreateDatabaseResponse.json"); - Server.Given(CreatePostRequestBuilder(path)) - .RespondWith( + Server.Given(CreatePostRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var createDatabaseParameters = new DatabasesCreateParameters(); - - createDatabaseParameters.Parent = new ParentPageInput - { - PageId = pageId - }; - - createDatabaseParameters.Title = new List + var createDatabaseParameters = new DatabasesCreateParameters + { + Parent = new ParentPageInput { PageId = pageId }, + Title = new List { new RichTextTextInput { @@ -198,29 +204,30 @@ public async Task CreateDatabaseAsync() Link = null } } - }; - - createDatabaseParameters.Properties = new Dictionary + }, + Properties = new Dictionary { { "Name", new TitlePropertySchema { Title = new Dictionary() } }, - { "Price", new NumberPropertySchema { Number = new Number { Format = NumberFormat.Dollar } } }, - { "Food group", new SelectPropertySchema + { "Price", new NumberPropertySchema { Number = new Number { Format = "dollar" } } }, + { + "Food group", + new SelectPropertySchema { Select = new OptionWrapper { Options = new List { - new SelectOptionSchema + new() { Color = Color.Green, Name = "🥦Vegetable" }, - new SelectOptionSchema + new() { Color = Color.Red, Name = "🍎Fruit" }, - new SelectOptionSchema + new() { Color = Color.Yellow, Name = "💪Protein" @@ -229,87 +236,91 @@ public async Task CreateDatabaseAsync() } } }, - { "Last ordered", new DatePropertySchema{ Date = new Dictionary() } } - }; + { "Last ordered", new DatePropertySchema { Date = new Dictionary() } } + } + }; - var database = await _client.CreateAsync(createDatabaseParameters); + var database = await _client.CreateAsync(createDatabaseParameters); - database.Parent.Type.Should().Be(ParentType.PageId); - database.Parent.Should().BeOfType(); - ((PageParent)database.Parent).PageId.Should().Be(pageId); + database.Parent.Type.Should().Be(ParentType.PageId); + database.Parent.Should().BeOfType(); + ((PageParent)database.Parent).PageId.Should().Be(pageId); - database.Properties.Should().HaveCount(4); + database.Properties.Should().HaveCount(4); - var selectOptions = (SelectProperty)database.Properties["Food group"]; - selectOptions.Name.Should().Be("Food group"); - selectOptions.Select.Options.Should().SatisfyRespectively( - option => - { - option.Name.Should().Be("🥦Vegetable"); - option.Color.Should().Be(Color.Green); - }, - option => - { - option.Name.Should().Be("🍎Fruit"); - option.Color.Should().Be(Color.Red); - }, - option => - { - option.Name.Should().Be("💪Protein"); - option.Color.Should().Be(Color.Yellow); - } - ); - } + var selectOptions = (SelectProperty)database.Properties["Food group"]; + selectOptions.Name.Should().Be("Food group"); - [Fact] - public async Task UpdateDatabaseAsync() - { - var databaseId = "1e9eee34-9c5c-4fe6-a4e1-8244eb141ed8"; - var path = ApiEndpoints.DatabasesApiUrls.Update(databaseId); - var jsonData = await File.ReadAllTextAsync("data/databases/UpdateDatabaseResponse.json"); + selectOptions.Select.Options.Should().SatisfyRespectively( + option => + { + option.Name.Should().Be("🥦Vegetable"); + option.Color.Should().Be(Color.Green); + }, + option => + { + option.Name.Should().Be("🍎Fruit"); + option.Color.Should().Be(Color.Red); + }, + option => + { + option.Name.Should().Be("💪Protein"); + option.Color.Should().Be(Color.Yellow); + } + ); + } - Server.Given(CreatePatchRequestBuilder(path)) - .RespondWith( + [Fact] + public async Task UpdateDatabaseAsync() + { + var databaseId = "1e9eee34-9c5c-4fe6-a4e1-8244eb141ed8"; + var path = ApiEndpoints.DatabasesApiUrls.Update(databaseId); + var jsonData = await File.ReadAllTextAsync("data/databases/UpdateDatabaseResponse.json"); + + Server.Given(CreatePatchRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var updateDatabaseParameters = new DatabasesUpdateParameters(); - - updateDatabaseParameters.Title = new List - { - new RichTextTextInput + var updateDatabaseParameters = new DatabasesUpdateParameters + { + Title = + new List { - Text = new Text + new RichTextTextInput { - Content = "Grocery List New", - Link = null + Text = new Text + { + Content = "Grocery List New", + Link = null + } } - } - }; - - updateDatabaseParameters.Properties = new Dictionary + }, + Properties = new Dictionary { { "Name", new TitleUpdatePropertySchema { Title = new Dictionary() } }, - { "Price", new NumberUpdatePropertySchema { Number = new Number { Format = NumberFormat.Yen } } }, - { "Food group", new SelectUpdatePropertySchema + { "Price", new NumberUpdatePropertySchema { Number = new Number { Format = "yen" } } }, + { + "Food group", + new SelectUpdatePropertySchema { Select = new OptionWrapper { Options = new List { - new SelectOption + new() { Color = Color.Green, Name = "🥦Vegetables" }, - new SelectOption + new() { Color = Color.Red, Name = "🍎Fruit" }, - new SelectOption + new() { Color = Color.Yellow, Name = "💪Protein" @@ -318,72 +329,73 @@ public async Task UpdateDatabaseAsync() } } }, - { "Last ordered", new DateUpdatePropertySchema{ Date = new Dictionary() } } - }; + { "Last ordered", new DateUpdatePropertySchema { Date = new Dictionary() } } + } + }; - var database = await _client.UpdateAsync(databaseId, updateDatabaseParameters); + var database = await _client.UpdateAsync(databaseId, updateDatabaseParameters); - database.Parent.Type.Should().Be(ParentType.PageId); - database.Parent.Should().BeOfType(); - ((PageParent)database.Parent).PageId.Should().Be("533578e3-edf1-4c0a-91a9-da6b09bac3ee"); + database.Parent.Type.Should().Be(ParentType.PageId); + database.Parent.Should().BeOfType(); + ((PageParent)database.Parent).PageId.Should().Be("533578e3-edf1-4c0a-91a9-da6b09bac3ee"); - database.Properties.Should().HaveCount(4); + database.Properties.Should().HaveCount(4); - database.Title.Should().ContainSingle(); - database.Title.Should().SatisfyRespectively( - title => - { - title.Should().BeAssignableTo(); - ((RichTextText)title).Text.Content.Should().Be("Grocery List New"); - } - ); + database.Title.Should().ContainSingle(); - var selectOptions = (SelectProperty)database.Properties["Food group"]; - selectOptions.Name.Should().Be("Food group"); - selectOptions.Select.Options.Should().SatisfyRespectively( - option => - { - option.Name.Should().Be("🥦Vegetables"); - option.Color.Should().Be(Color.Green); - }, - option => - { - option.Name.Should().Be("🍎Fruit"); - option.Color.Should().Be(Color.Red); - }, - option => - { - option.Name.Should().Be("💪Protein"); - option.Color.Should().Be(Color.Yellow); - } - ); + database.Title.Should().SatisfyRespectively( + title => + { + title.Should().BeAssignableTo(); + ((RichTextText)title).Text.Content.Should().Be("Grocery List New"); + } + ); - var price = (NumberProperty)database.Properties["Price"]; - price.Number.Format.Should().Be(NumberFormat.Yen); - } + var selectOptions = (SelectProperty)database.Properties["Food group"]; + selectOptions.Name.Should().Be("Food group"); - [Fact] - public async Task FormulaPropertyCanBeSetWhenCreatingDatabase() - { - var pageId = "98ad959b-2b6a-4774-80ee-00246fb0ea9b"; - var path = ApiEndpoints.DatabasesApiUrls.Create; - var jsonData = await File.ReadAllTextAsync("data/databases/FormulaPropertyCanBeSetWhenCreatingDatabaseResponse.json"); + selectOptions.Select.Options.Should().SatisfyRespectively( + option => + { + option.Name.Should().Be("🥦Vegetables"); + option.Color.Should().Be(Color.Green); + }, + option => + { + option.Name.Should().Be("🍎Fruit"); + option.Color.Should().Be(Color.Red); + }, + option => + { + option.Name.Should().Be("💪Protein"); + option.Color.Should().Be(Color.Yellow); + } + ); - Server.Given(CreatePostRequestBuilder(path)) - .RespondWith( + var price = (NumberProperty)database.Properties["Price"]; + price.Number.Format.Should().Be("yen"); + } + + [Fact] + public async Task FormulaPropertyCanBeSetWhenCreatingDatabase() + { + var pageId = "98ad959b-2b6a-4774-80ee-00246fb0ea9b"; + var path = ApiEndpoints.DatabasesApiUrls.Create; + + var jsonData + = await File.ReadAllTextAsync("data/databases/FormulaPropertyCanBeSetWhenCreatingDatabaseResponse.json"); + + Server.Given(CreatePostRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var createDatabaseParameters = new DatabasesCreateParameters(); - - createDatabaseParameters.Parent = new ParentPageInput - { - PageId = pageId - }; - - createDatabaseParameters.Title = new List + var createDatabaseParameters = new DatabasesCreateParameters + { + Parent = new ParentPageInput { PageId = pageId }, + Title = new List { new RichTextTextInput { @@ -393,78 +405,117 @@ public async Task FormulaPropertyCanBeSetWhenCreatingDatabase() Link = null } } - }; - - createDatabaseParameters.Properties = new Dictionary + }, + Properties = new Dictionary { - { "Cost of next trip", new FormulaPropertySchema { Formula = new Formula { Expression = "if(prop(\"In stock\"), 0, prop(\"Price\"))" } } }, - { "Price", new NumberPropertySchema { Number = new Number { Format = NumberFormat.Dollar } } } - }; + { + "Cost of next trip", + new FormulaPropertySchema + { + Formula = new Formula { Expression = "if(prop(\"In stock\"), 0, prop(\"Price\"))" } + } + }, + { "Price", new NumberPropertySchema { Number = new Number { Format = "dollar" } } } + } + }; - var database = await _client.CreateAsync(createDatabaseParameters); + var database = await _client.CreateAsync(createDatabaseParameters); - database.Parent.Type.Should().Be(ParentType.PageId); - database.Parent.Should().BeOfType(); - ((PageParent)database.Parent).PageId.Should().Be(pageId); + database.Parent.Type.Should().Be(ParentType.PageId); + database.Parent.Should().BeOfType(); + ((PageParent)database.Parent).PageId.Should().Be(pageId); - database.Properties.Should().HaveCount(2); + database.Properties.Should().HaveCount(2); - var formulaProperty = (FormulaProperty)database.Properties["Cost of next trip"]; - formulaProperty.Formula.Expression.Should().Be("if(prop(\"In stock\"), 0, prop(\"Price\"))"); - } + var formulaProperty = (FormulaProperty)database.Properties["Cost of next trip"]; + formulaProperty.Formula.Expression.Should().Be("if(prop(\"In stock\"), 0, prop(\"Price\"))"); + } - [Fact] - public async Task Fix123_QueryAsync_DateFormulaValue_Returns_Null() - { - var databaseId = "f86f2262-0751-40f2-8f63-e3f7a3c39fcb"; - var path = ApiEndpoints.DatabasesApiUrls.Query(databaseId); - var jsonData = await File.ReadAllTextAsync("data/databases/Fix123QueryAsyncDateFormulaValueReturnsNullResponse.json"); + [Fact] + public async Task Fix123_QueryAsync_DateFormulaValue_Returns_Null() + { + var databaseId = "f86f2262-0751-40f2-8f63-e3f7a3c39fcb"; + var path = ApiEndpoints.DatabasesApiUrls.Query(databaseId); - Server.Given(CreatePostRequestBuilder(path)) - .RespondWith( + var jsonData + = await File.ReadAllTextAsync("data/databases/Fix123QueryAsyncDateFormulaValueReturnsNullResponse.json"); + + Server.Given(CreatePostRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var databasesQueryParams = new DatabasesQueryParameters + var databasesQueryParams = new DatabasesQueryParameters + { + Filter = new CompoundFilter { - Filter = new CompoundFilter + Or = new List { - Or = new List { - new CheckboxFilter( - "In stock", - true - ), - new NumberFilter( - "Cost of next trip", - greaterThanOrEqualTo: 2 - ) - }, - }, - Sorts = new List + new CheckboxFilter( + "In stock", + true + ), + new NumberFilter( + "Cost of next trip", + greaterThanOrEqualTo: 2 + ) + } + }, + Sorts = new List + { + new() { - new Sort - { - Property = "Last ordered", - Direction = Direction.Ascending - } + Property = "Last ordered", + Direction = Direction.Ascending } - }; + } + }; - var pagesPaginatedList = await _client.QueryAsync(databaseId, databasesQueryParams); + var pagesPaginatedList = await _client.QueryAsync(databaseId, databasesQueryParams); - pagesPaginatedList.Results.Should().ContainSingle(); + pagesPaginatedList.Results.Should().ContainSingle(); - foreach (var page in pagesPaginatedList.Results) - { - page.Parent.Should().BeAssignableTo(); - page.Object.Should().Be(ObjectType.Page); + foreach (var iWikiDatabase in pagesPaginatedList.Results) + { + var page = (Page)iWikiDatabase; + page.Parent.Should().BeAssignableTo(); + page.Object.Should().Be(ObjectType.Page); - var formulaPropertyValue = (FormulaPropertyValue)page.Properties["FormulaProp"]; - formulaPropertyValue.Formula.Date.Start.Should().Be(DateTime.Parse("2021-06-28")); - formulaPropertyValue.Formula.Date.End.Should().BeNull(); - } + Server.Given(CreateGetRequestBuilder( + ApiEndpoints.PagesApiUrls.RetrievePropertyItem(page.Id, page.Properties["FormulaProp"].Id))) + .RespondWith( + Response.Create() + .WithStatusCode(200) + .WithBody( + "{\"object\":\"property_item\",\"id\":\"JwY^\",\"type\":\"formula\",\"formula\":{\"type\":\"date\",\"date\":{\"start\":\"2021-06-28\",\"end\":null}}}") + ); + + var formulaPropertyValue = (FormulaPropertyItem)await _pagesClient.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = page.Properties["FormulaProp"].Id + }); + + //var formulaPropertyValue = (FormulaPropertyValue)page.Properties["FormulaProp"]; + formulaPropertyValue.Formula.Date.Start.Should().Be(DateTimeOffset.Parse("2021-06-28", null, System.Globalization.DateTimeStyles.AssumeUniversal).UtcDateTime); + formulaPropertyValue.Formula.Date.End.Should().BeNull(); } } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData(" ")] + public async Task RetrieveAsync_throws_argument_null_exception_if_database_id_is_null_or_empty(string databaseId) + { + // Arrange && Act + async Task Act() => await _client.RetrieveAsync(databaseId); + + // Assert + var exception = await Assert.ThrowsAsync(Act); + Assert.Equal("databaseId", exception.ParamName); + } } diff --git a/Test/Notion.UnitTests/DateCustomConverterTests.cs b/Test/Notion.UnitTests/DateCustomConverterTests.cs new file mode 100644 index 00000000..81c33f57 --- /dev/null +++ b/Test/Notion.UnitTests/DateCustomConverterTests.cs @@ -0,0 +1,219 @@ +using System; +using System.IO; +using Newtonsoft.Json; +using Notion.Client; +using Xunit; + +namespace NotionUnitTests.PropertyValue; + +public class DateCustomConverterTests +{ + private readonly DateCustomConverter _converter = new(); + private readonly JsonSerializer _serializer = new(); + + [Fact] + public void Serialize_null_writes_null() + { + // Arrange + Date date = null; + var stringWriter = new StringWriter(); + var jsonWriter = new JsonTextWriter(stringWriter); + + // Act + _converter.WriteJson(jsonWriter, date, _serializer); + jsonWriter.Flush(); + + // Assert + Assert.Equal("null", stringWriter.ToString()); + } + + [Fact] + public void Serialize_start_date_only_produces_correct_json() + { + // Arrange + var date = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 0, 0, 0, TimeSpan.Zero), + IncludeTime = false + }; + + // Act + var json = JsonConvert.SerializeObject(date); + + // Assert + Assert.Contains("\"start\":\"2023-05-15\"", json); + Assert.DoesNotContain("\"end\":", json); + Assert.DoesNotContain("\"time_zone\":", json); + } + + [Fact] + public void Serialize_start_and_end_dates_produces_correct_json() + { + // Arrange + var date = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 0, 0, 0, TimeSpan.Zero), + End = new DateTimeOffset(2023, 5, 20, 0, 0, 0, TimeSpan.Zero), + IncludeTime = false + }; + + // Act + var json = JsonConvert.SerializeObject(date); + + // Assert + Assert.Contains("\"start\":\"2023-05-15\"", json); + Assert.Contains("\"end\":\"2023-05-20\"", json); + Assert.DoesNotContain("\"time_zone\":", json); + } + + [Fact] + public void Serialize_with_time_included_formats_time_correctly() + { + // Arrange + var date = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), + IncludeTime = true + }; + + // Act + var json = JsonConvert.SerializeObject(date); + + // Assert + Assert.Contains("\"start\":\"2023-05-15T14:30:45Z\"", json); + } + + [Fact] + public void Serialize_with_time_zone_includes_time_zone() + { + // Arrange + var date = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), + TimeZone = "Europe/London", + IncludeTime = true + }; + + // Act + var json = JsonConvert.SerializeObject(date); + + // Assert + Assert.Contains("\"start\":\"2023-05-15T14:30:45Z\"", json); + Assert.Contains("\"time_zone\":\"Europe/London\"", json); + } + + [Fact] + public void Deserialize_null_returns_null() + { + // Arrange + const string Json = "null"; + + // Act + var result = JsonConvert.DeserializeObject(Json); + + // Assert + Assert.Null(result); + } + + [Fact] + public void Deserialize_start_date_only_returns_correct_date() + { + // Arrange + const string Json = "{\"start\":\"2023-05-15\"}"; + + // Act + var result = JsonConvert.DeserializeObject(Json); + + // Assert + Assert.NotNull(result); + Assert.Equal(new DateTimeOffset(2023, 5, 15, 0, 0, 0, TimeSpan.Zero), result.Start); + Assert.Null(result.End); + Assert.Null(result.TimeZone); + Assert.False(result.IncludeTime); + } + + [Fact] + public void Deserialize_with_time_sets_include_time_flag() + { + // Arrange + const string Json = "{\"start\":\"2023-05-15T14:30:45\"}"; + + // Act + var result = JsonConvert.DeserializeObject(Json); + + // Assert + Assert.NotNull(result); + Assert.Equal(new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), result.Start); + Assert.True(result.IncludeTime); + } + + [Fact] + public void Deserialize_with_start_end_and_time_zone_returns_complete_date() + { + // Arrange + const string Json = "{\"start\":\"2023-05-15T14:30:45\",\"end\":\"2023-05-20T16:45:00\",\"time_zone\":\"America/New_York\"}"; + + // Act + var result = JsonConvert.DeserializeObject(Json); + + // Assert + Assert.NotNull(result); + Assert.Equal(new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), result.Start); + Assert.Equal(new DateTimeOffset(2023, 5, 20, 16, 45, 0, TimeSpan.Zero), result.End); + Assert.Equal("America/New_York", result.TimeZone); + Assert.True(result.IncludeTime); + } + + [Fact] + public void Date_property_value_serialize_deserialize_maintains_data() + { + // Arrange + var datePropertyValue = new DatePropertyValue + { + Date = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), + End = new DateTimeOffset(2023, 5, 20, 16, 45, 0, TimeSpan.Zero), + TimeZone = "Europe/Berlin", + IncludeTime = true + } + }; + + // Act + var json = JsonConvert.SerializeObject(datePropertyValue); + var result = JsonConvert.DeserializeObject(json); + + // Assert + Assert.NotNull(result); + Assert.Equal(PropertyValueType.Date, result.Type); + Assert.NotNull(result.Date); + Assert.Equal(datePropertyValue.Date.Start, result.Date.Start); + Assert.Equal(datePropertyValue.Date.End, result.Date.End); + Assert.Equal(datePropertyValue.Date.TimeZone, result.Date.TimeZone); + Assert.Equal(datePropertyValue.Date.IncludeTime, result.Date.IncludeTime); + } + + [Fact] + public void Round_trip_preserves_data() + { + // Arrange + var originalDate = new Date + { + Start = new DateTimeOffset(2023, 5, 15, 14, 30, 45, TimeSpan.Zero), + End = new DateTimeOffset(2023, 5, 20, 16, 45, 0, TimeSpan.Zero), + TimeZone = "Europe/Berlin", + IncludeTime = true + }; + + // Act + var json = JsonConvert.SerializeObject(originalDate); + var deserializedDate = JsonConvert.DeserializeObject(json); + + // Assert + Assert.NotNull(deserializedDate); + Assert.Equal(originalDate.Start, deserializedDate.Start); + Assert.Equal(originalDate.End, deserializedDate.End); + Assert.Equal(originalDate.TimeZone, deserializedDate.TimeZone); + Assert.True(deserializedDate.IncludeTime); + } +} diff --git a/Test/Notion.UnitTests/FilterTests.cs b/Test/Notion.UnitTests/FilterTests.cs index 38a0921b..39b90229 100644 --- a/Test/Notion.UnitTests/FilterTests.cs +++ b/Test/Notion.UnitTests/FilterTests.cs @@ -4,131 +4,146 @@ using Notion.Client; using Xunit; -namespace Notion.UnitTests +namespace Notion.UnitTests; + +public class FilterTests { - public class SerializerSettingsSource : RestClient + private readonly SerializerSettingsSource _settingsSource = new(new ClientOptions()); + + private string SerializeFilter(Filter filter) { - public SerializerSettingsSource(ClientOptions options) : base(options) - { + return JsonConvert.SerializeObject(filter, _settingsSource.GetSerializerSettings()); + } - } + [Fact] + public void CompoundFilterTest() + { + var selectFilter = new SelectFilter("A select", "Option"); + var relationFilter = new RelationFilter("Link", "subtask#1"); + var dateFilter = new DateFilter("Due", pastMonth: new Dictionary()); - public JsonSerializerSettings GetSerializerSettings() + var filterGroup = new List { - return defaultSerializerSettings; - } + relationFilter, + selectFilter + }; + + var complexFiler = new CompoundFilter( + and: new List + { + dateFilter, + new CompoundFilter(filterGroup) + } + ); + + Assert.Equal( + "{\"and\":[{\"date\":{\"past_month\":{}},\"property\":\"Due\"}," + + "{\"or\":[{\"relation\":{\"contains\":\"subtask#1\"},\"property\":\"Link\"}," + + "{\"select\":{\"equals\":\"Option\"},\"property\":\"A select\"}]}]}", + SerializeFilter(complexFiler) + ); + } + [Fact] + public void CheckboxFilterTest() + { + var filter = new CheckboxFilter("Property name", false); + + Assert.Equal( + "{\"checkbox\":{\"equals\":false},\"property\":\"Property name\"}", + SerializeFilter(filter) + ); } - public class FilterTests + + [Fact] + public void DateFilterTest() { - private readonly SerializerSettingsSource _settingsSource = new SerializerSettingsSource(new ClientOptions()); + var filter = new DateFilter("When", onOrAfter: new DateTime(2042, 11, 29)); - private string SerializeFilter(Filter filter) - { - return JsonConvert.SerializeObject(filter, _settingsSource.GetSerializerSettings()); - } + Assert.Equal( + "{\"date\":{\"on_or_after\":\"2042-11-29T00:00:00\"},\"property\":\"When\"}", + SerializeFilter(filter) + ); + } - [Fact] - public void CompoundFilterTest() - { - var selectFilter = new SelectFilter("A select", equal: "Option"); - var relationFilter = new RelationFilter("Link", contains: "subtask#1"); - var dateFilter = new DateFilter("Due", pastMonth: new Dictionary()); - - var filterGroup = new List { relationFilter, selectFilter }; - var complexFiler = new CompoundFilter( - and: new List { dateFilter, new CompoundFilter(or: filterGroup) } - ); - - Assert.Equal( - "{\"and\":[{\"date\":{\"past_month\":{}},\"property\":\"Due\"}," - + "{\"or\":[{\"relation\":{\"contains\":\"subtask#1\"},\"property\":\"Link\"}," + - "{\"select\":{\"equals\":\"Option\"},\"property\":\"A select\"}]}]}", - SerializeFilter(complexFiler) - ); - } + [Fact] + public void FilesFilterTest() + { + var filter = new FilesFilter("Attachments", isNotEmpty: false); - [Fact] - public void CheckboxFilterTest() - { - var filter = new CheckboxFilter("Property name", equal: false); - Assert.Equal( - "{\"checkbox\":{\"equals\":false},\"property\":\"Property name\"}", - SerializeFilter(filter) - ); - } + Assert.Equal( + "{\"files\":{\"is_not_empty\":false},\"property\":\"Attachments\"}", + SerializeFilter(filter) + ); + } - [Fact] - public void DateFilterTest() - { - var filter = new DateFilter("When", onOrAfter: new DateTime(2042, 11, 29)); - Assert.Equal( - "{\"date\":{\"on_or_after\":\"2042-11-29T00:00:00\"},\"property\":\"When\"}", - SerializeFilter(filter) - ); - } + [Fact] + public void FormulaFilterTest() + { + var filter = new FormulaFilter( + "Some", + number: new NumberFilter.Condition(isEmpty: true) + ); + + Assert.Equal( + "{\"formula\":{\"number\":{\"is_empty\":true}},\"property\":\"Some\"}", + SerializeFilter(filter) + ); + } - [Fact] - public void FilesFilterTest() - { - var filter = new FilesFilter("Attachments", isNotEmpty: false); - Assert.Equal( - "{\"files\":{\"is_not_empty\":false},\"property\":\"Attachments\"}", - SerializeFilter(filter) - ); - } + [Fact] + public void MultiSelectFilterTest() + { + var filter = new MultiSelectFilter("category 1", doesNotContain: "tag"); - [Fact] - public void FormulaFilterTest() - { - var filter = new FormulaFilter( - "Some", - number: new NumberFilter.Condition(isEmpty: true) - ); - Assert.Equal( - "{\"formula\":{\"number\":{\"is_empty\":true}},\"property\":\"Some\"}", - SerializeFilter(filter) - ); - } + Assert.Equal( + "{\"multi_select\":{\"does_not_contain\":\"tag\"},\"property\":\"category 1\"}", + SerializeFilter(filter) + ); + } - [Fact] - public void MultiSelectFilterTest() - { - var filter = new MultiSelectFilter("category 1", doesNotContain: "tag"); - Assert.Equal( - "{\"multi_select\":{\"does_not_contain\":\"tag\"},\"property\":\"category 1\"}", - SerializeFilter(filter) - ); - } + [Fact(Skip = "Not sure if integer should be serialized as a number with decimals")] + public void NumberFilterTest() + { + var filter = new NumberFilter("sum", greaterThanOrEqualTo: -54); - [Fact(Skip = "Not sure if integer should be serialized as a number with decimals")] - public void NumberFilterTest() - { - var filter = new NumberFilter("sum", greaterThanOrEqualTo: -54); - Assert.Equal( - "{\"number\":{\"greater_than_or_equal_to\":-54.0},\"property\":\"sum\"}", - SerializeFilter(filter) - ); - } + Assert.Equal( + "{\"number\":{\"greater_than_or_equal_to\":-54.0},\"property\":\"sum\"}", + SerializeFilter(filter) + ); + } + + [Fact] + public void PeopleFilter() + { + var filter = new PeopleFilter("assignee PM", doesNotContain: "some-uuid"); + + Assert.Equal( + "{\"people\":{\"does_not_contain\":\"some-uuid\"},\"property\":\"assignee PM\"}", + SerializeFilter(filter) + ); + } + + [Fact] + public void RichTextFilterTest() + { + var filter = new RichTextFilter("Some property", doesNotEqual: "Example text"); + + Assert.Equal( + "{\"rich_text\":{\"does_not_equal\":\"Example text\"},\"property\":\"Some property\"}", + SerializeFilter(filter) + ); + } - [Fact] - public void PeopleFilter() + private class SerializerSettingsSource : RestClient + { + public SerializerSettingsSource(ClientOptions options) : base(options) { - var filter = new PeopleFilter("assignee PM", doesNotContain: "some-uuid"); - Assert.Equal( - "{\"people\":{\"does_not_contain\":\"some-uuid\"},\"property\":\"assignee PM\"}", - SerializeFilter(filter) - ); } - [Fact] - public void TextFilterTest() + public JsonSerializerSettings GetSerializerSettings() { - var filter = new TextFilter("Some property", doesNotEqual: "Example text"); - Assert.Equal( - "{\"text\":{\"does_not_equal\":\"Example text\"},\"property\":\"Some property\"}", - SerializeFilter(filter) - ); + return DefaultSerializerSettings; } } -} +} diff --git a/Test/Notion.UnitTests/HelperAsserts.cs b/Test/Notion.UnitTests/HelperAsserts.cs index 1b07fbf5..3b1a29ec 100644 --- a/Test/Notion.UnitTests/HelperAsserts.cs +++ b/Test/Notion.UnitTests/HelperAsserts.cs @@ -1,41 +1,44 @@ using FluentAssertions; using Notion.Client; -namespace Notion.UnitTests +namespace Notion.UnitTests; + +public static class HelperAsserts { - public static class HelperAsserts + public static void IPageIconAsserts(IPageIcon icon) { - public static void IPageIconAsserts(IPageIcon icon) + icon.Should().NotBeNull(); + + switch (icon) { - icon.Should().NotBeNull(); - - switch (icon) - { - case EmojiObject emoji: - emoji.Emoji.Should().NotBeNull(); - break; - case FileObject fileObject: - FileObjectAsserts(fileObject); - break; - } + case EmojiObject emoji: + emoji.Emoji.Should().NotBeNull(); + + break; + case FileObject fileObject: + FileObjectAsserts(fileObject); + + break; } + } - public static void FileObjectAsserts(FileObject fileObject) + public static void FileObjectAsserts(FileObject fileObject) + { + fileObject.Should().NotBeNull(); + + switch (fileObject) { - fileObject.Should().NotBeNull(); - - switch (fileObject) - { - case UploadedFile uploadedFile: - uploadedFile.File.Should().NotBeNull(); - uploadedFile.File.Url.Should().NotBeNull(); - uploadedFile.File.ExpiryTime.Should().NotBeSameDateAs(default); - break; - case ExternalFile externalFile: - externalFile.External.Should().NotBeNull(); - externalFile.External.Url.Should().NotBeNull(); - break; - } + case UploadedFile uploadedFile: + uploadedFile.File.Should().NotBeNull(); + uploadedFile.File.Url.Should().NotBeNull(); + uploadedFile.File.ExpiryTime.Should().NotBeSameDateAs(default); + + break; + case ExternalFile externalFile: + externalFile.External.Should().NotBeNull(); + externalFile.External.Url.Should().NotBeNull(); + + break; } } } diff --git a/Test/Notion.UnitTests/Notion.UnitTests.csproj b/Test/Notion.UnitTests/Notion.UnitTests.csproj index 3b729bf5..6a33f5c0 100644 --- a/Test/Notion.UnitTests/Notion.UnitTests.csproj +++ b/Test/Notion.UnitTests/Notion.UnitTests.csproj @@ -1,22 +1,22 @@ - net5.0 + net6.0 false - - + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -51,7 +51,7 @@ Always - + Always @@ -93,6 +93,9 @@ Always + + Always + diff --git a/Test/Notion.UnitTests/PagesClientTests.cs b/Test/Notion.UnitTests/PagesClientTests.cs index e80240d3..4dc75380 100644 --- a/Test/Notion.UnitTests/PagesClientTests.cs +++ b/Test/Notion.UnitTests/PagesClientTests.cs @@ -8,227 +8,262 @@ using WireMock.ResponseBuilders; using Xunit; -namespace Notion.UnitTests +namespace Notion.UnitTests; + +public class PagesClientTests : ApiTestBase { - public class PagesClientTests : ApiTestBase - { - private readonly IPagesClient _client; + private readonly IPagesClient _client; - public PagesClientTests() - { - _client = new PagesClient(new RestClient(ClientOptions)); - } + public PagesClientTests() + { + _client = new PagesClient(new RestClient(ClientOptions)); + } - [Fact] - public async Task RetrieveAsync() - { - var pageId = "251d2b5f-268c-4de2-afe9-c71ff92ca95c"; - var path = ApiEndpoints.PagesApiUrls.Retrieve(pageId); - var jsonData = await File.ReadAllTextAsync("data/pages/PageObjectShouldHaveUrlPropertyResponse.json"); + [Fact] + public async Task RetrieveAsync() + { + var pageId = "251d2b5f-268c-4de2-afe9-c71ff92ca95c"; + var path = ApiEndpoints.PagesApiUrls.Retrieve(pageId); + var jsonData = await File.ReadAllTextAsync("data/pages/PageObjectShouldHaveUrlPropertyResponse.json"); - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var page = await _client.RetrieveAsync(pageId); + var page = await _client.RetrieveAsync(pageId); - page.Url.Should().Be("https://www.notion.so/Avocado-251d2b5f268c4de2afe9c71ff92ca95c"); - page.Id.Should().Be(pageId); - page.Parent.Type.Should().Be(ParentType.DatabaseId); - ((DatabaseParent)page.Parent).DatabaseId.Should().Be("48f8fee9-cd79-4180-bc2f-ec0398253067"); - page.IsArchived.Should().BeFalse(); - } + page.Url.Should().Be("https://www.notion.so/Avocado-251d2b5f268c4de2afe9c71ff92ca95c"); + page.Id.Should().Be(pageId); + page.Parent.Type.Should().Be(ParentType.DatabaseId); + ((DatabaseParent)page.Parent).DatabaseId.Should().Be("48f8fee9-cd79-4180-bc2f-ec0398253067"); + page.InTrash.Should().BeFalse(); + } - [Fact] - public async Task CreateAsync() - { - var path = ApiEndpoints.PagesApiUrls.Create(); + [Fact] + public async Task CreateAsync() + { + var path = ApiEndpoints.PagesApiUrls.Create(); - var jsonData = await File.ReadAllTextAsync("data/pages/CreatePageResponse.json"); + var jsonData = await File.ReadAllTextAsync("data/pages/CreatePageResponse.json"); - Server.Given(CreatePostRequestBuilder(path)) - .RespondWith( - Response.Create() + Server.Given(CreatePostRequestBuilder(path)) + .RespondWith( + Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var pagesCreateParameters = PagesCreateParametersBuilder.Create(new DatabaseParentInput - { - DatabaseId = "3c357473-a281-49a4-88c0-10d2b245a589" - }).AddProperty("Name", new TitlePropertyValue() - { - Title = new List() + var pagesCreateParameters = PagesCreateParametersBuilder + .Create(new DatabaseParentInput { DatabaseId = "3c357473-a281-49a4-88c0-10d2b245a589" }) + .AddProperty( + "Name", + new TitlePropertyValue { - new RichTextText() - { - Text = new Text - { - Content = "Test" - } - } + Title = new List { new RichTextText { Text = new Text { Content = "Test" } } } } - }).Build(); + ).Build(); - var page = await _client.CreateAsync(pagesCreateParameters); + var page = await _client.CreateAsync(pagesCreateParameters); - page.Id.Should().NotBeNullOrEmpty(); - page.Url.Should().NotBeNullOrEmpty(); - page.Properties.Should().HaveCount(1); - page.Properties.First().Key.Should().Be("Name"); - page.IsArchived.Should().BeFalse(); - page.Parent.Should().NotBeNull(); - ((DatabaseParent)page.Parent).DatabaseId.Should().Be("3c357473-a281-49a4-88c0-10d2b245a589"); - } + page.Id.Should().NotBeNullOrEmpty(); + page.Url.Should().NotBeNullOrEmpty(); + page.Properties.Should().HaveCount(1); + page.Properties.First().Key.Should().Be("Name"); + page.InTrash.Should().BeFalse(); + page.Parent.Should().NotBeNull(); + ((DatabaseParent)page.Parent).DatabaseId.Should().Be("3c357473-a281-49a4-88c0-10d2b245a589"); + } - [Fact] - public async Task UpdatePropertiesAsync() - { - var pageId = "251d2b5f-268c-4de2-afe9-c71ff92ca95c"; - var path = ApiEndpoints.PagesApiUrls.UpdateProperties(pageId); + [Fact] + public async Task UpdatePropertiesAsync() + { + var pageId = "251d2b5f-268c-4de2-afe9-c71ff92ca95c"; + var propertyId = "{>U;"; + var path = ApiEndpoints.PagesApiUrls.UpdateProperties(pageId); - var jsonData = await File.ReadAllTextAsync("data/pages/UpdatePagePropertiesResponse.json"); + var jsonData = await File.ReadAllTextAsync("data/pages/UpdatePagePropertiesResponse.json"); - Server.Given(CreatePatchRequestBuilder(path)) - .RespondWith( - Response.Create() + Server.Given(CreatePatchRequestBuilder(path)) + .RespondWith( + Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var updatedProperties = new Dictionary() - { - { "In stock", new CheckboxPropertyValue() { Checkbox = true } } - }; + Server.Given(CreateGetRequestBuilder(ApiEndpoints.PagesApiUrls.RetrievePropertyItem(pageId, propertyId))) + .RespondWith( + Response.Create().WithStatusCode(200) + .WithBody( + "{\"object\":\"property_item\",\"id\":\"{>U;\",\"type\":\"checkbox\",\"checkbox\":true}")); + + var updatedProperties = new Dictionary + { + { "In stock", new CheckboxPropertyValue { Checkbox = true } } + }; - var page = await _client.UpdatePropertiesAsync(pageId, updatedProperties); + var page = await _client.UpdatePropertiesAsync(pageId, updatedProperties); - page.Id.Should().Be(pageId); - page.Properties.Should().HaveCount(2); - var updatedProperty = page.Properties.First(x => x.Key == "In stock"); - ((CheckboxPropertyValue)updatedProperty.Value).Checkbox.Should().BeTrue(); - } + page.Id.Should().Be(pageId); + page.Properties.Should().HaveCount(2); + var updatedProperty = page.Properties.First(x => x.Key == "In stock"); - [Fact] - public async Task PageObjectShouldHaveUrlProperty() - { - var pageId = "251d2b5f-268c-4de2-afe9-c71ff92ca95c"; - var path = ApiEndpoints.PagesApiUrls.Retrieve(pageId); - var jsonData = await File.ReadAllTextAsync("data/pages/PageObjectShouldHaveUrlPropertyResponse.json"); + var checkboxPropertyValue = (CheckboxPropertyItem)await _client.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = updatedProperty.Value.Id + }); - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( + checkboxPropertyValue.Checkbox.Should().BeTrue(); + } + + [Fact] + public async Task PageObjectShouldHaveUrlProperty() + { + var pageId = "251d2b5f-268c-4de2-afe9-c71ff92ca95c"; + var path = ApiEndpoints.PagesApiUrls.Retrieve(pageId); + var jsonData = await File.ReadAllTextAsync("data/pages/PageObjectShouldHaveUrlPropertyResponse.json"); + + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var page = await _client.RetrieveAsync(pageId); + var page = await _client.RetrieveAsync(pageId); - page.Url.Should().Be("https://www.notion.so/Avocado-251d2b5f268c4de2afe9c71ff92ca95c"); - } + page.Url.Should().Be("https://www.notion.so/Avocado-251d2b5f268c4de2afe9c71ff92ca95c"); + } - [Fact] - public async Task UpdatePageAsync() - { - var pageId = "251d2b5f-268c-4de2-afe9-c71ff92ca95c"; - var path = ApiEndpoints.PagesApiUrls.UpdateProperties(pageId); + [Fact] + public async Task UpdatePageAsync() + { + var pageId = "251d2b5f-268c-4de2-afe9-c71ff92ca95c"; + var propertyId = "{>U;"; + var path = ApiEndpoints.PagesApiUrls.UpdateProperties(pageId); - var jsonData = await File.ReadAllTextAsync("data/pages/UpdatePagePropertiesResponse.json"); + var jsonData = await File.ReadAllTextAsync("data/pages/UpdatePagePropertiesResponse.json"); - Server.Given(CreatePatchRequestBuilder(path)) - .RespondWith( - Response.Create() + Server.Given(CreatePatchRequestBuilder(path)) + .RespondWith( + Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var pagesUpdateParameters = new PagesUpdateParameters + Server.Given(CreateGetRequestBuilder(ApiEndpoints.PagesApiUrls.RetrievePropertyItem(pageId, propertyId))) + .RespondWith( + Response.Create().WithStatusCode(200) + .WithBody( + "{\"object\":\"property_item\",\"id\":\"{>U;\",\"type\":\"checkbox\",\"checkbox\":true}")); + + var pagesUpdateParameters = new PagesUpdateParameters + { + Properties = new Dictionary { - Properties = new Dictionary() - { - { "In stock", new CheckboxPropertyValue() { Checkbox = true } } - } - }; + { "In stock", new CheckboxPropertyValue { Checkbox = true } } + } + }; - var page = await _client.UpdateAsync(pageId, pagesUpdateParameters); + var page = await _client.UpdateAsync(pageId, pagesUpdateParameters); - page.Id.Should().Be(pageId); - page.IsArchived.Should().BeFalse(); - page.Properties.Should().HaveCount(2); - var updatedProperty = page.Properties.First(x => x.Key == "In stock"); - ((CheckboxPropertyValue)updatedProperty.Value).Checkbox.Should().BeTrue(); - } + page.Id.Should().Be(pageId); + page.InTrash.Should().BeFalse(); + page.Properties.Should().HaveCount(2); + var updatedProperty = page.Properties.First(x => x.Key == "In stock"); - [Fact] - public async Task ArchivePageAsync() - { - var pageId = "251d2b5f-268c-4de2-afe9-c71ff92ca95c"; - var path = ApiEndpoints.PagesApiUrls.UpdateProperties(pageId); + var checkboxPropertyValue = (CheckboxPropertyItem)await _client.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = updatedProperty.Value.Id + }); - var jsonData = await File.ReadAllTextAsync("data/pages/ArchivePageResponse.json"); + checkboxPropertyValue.Checkbox.Should().BeTrue(); + } - Server.Given(CreatePatchRequestBuilder(path)) - .RespondWith( - Response.Create() + [Fact] + public async Task TrashPageAsync() + { + var pageId = "251d2b5f-268c-4de2-afe9-c71ff92ca95c"; + var propertyId = "{>U;"; + + var path = ApiEndpoints.PagesApiUrls.UpdateProperties(pageId); + + var jsonData = await File.ReadAllTextAsync("data/pages/TrashPageResponse.json"); + + Server.Given(CreatePatchRequestBuilder(path)) + .RespondWith( + Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - var pagesUpdateParameters = new PagesUpdateParameters + Server.Given(CreateGetRequestBuilder(ApiEndpoints.PagesApiUrls.RetrievePropertyItem(pageId, propertyId))) + .RespondWith( + Response.Create().WithStatusCode(200) + .WithBody( + "{\"object\":\"property_item\",\"id\":\"{>U;\",\"type\":\"checkbox\",\"checkbox\":true}")); + + var pagesUpdateParameters = new PagesUpdateParameters + { + InTrash = true, + Properties = new Dictionary { - Archived = true, - Properties = new Dictionary() - { - { "In stock", new CheckboxPropertyValue() { Checkbox = true } } - } - }; + { "In stock", new CheckboxPropertyValue { Checkbox = true } } + } + }; - var page = await _client.UpdateAsync(pageId, pagesUpdateParameters); + var page = await _client.UpdateAsync(pageId, pagesUpdateParameters); - page.Id.Should().Be(pageId); - page.IsArchived.Should().BeTrue(); - page.Properties.Should().HaveCount(2); - var updatedProperty = page.Properties.First(x => x.Key == "In stock"); - ((CheckboxPropertyValue)updatedProperty.Value).Checkbox.Should().BeTrue(); - } + page.Id.Should().Be(pageId); + page.InTrash.Should().BeTrue(); + page.Properties.Should().HaveCount(3); + var updatedProperty = page.Properties.First(x => x.Key == "In stock"); - [Fact] - public async Task CreateAsync_Throws_ArgumentNullException_When_Parameter_Is_Null() - { - Func act = async () => await _client.CreateAsync(null); + var checkboxPropertyValue = (CheckboxPropertyItem)await _client.RetrievePagePropertyItemAsync( + new RetrievePropertyItemParameters + { + PageId = page.Id, + PropertyId = updatedProperty.Value.Id + }); - (await act.Should().ThrowAsync()).And.ParamName.Should().Be("pagesCreateParameters"); - } + checkboxPropertyValue.Checkbox.Should().BeTrue(); + } - [Fact] - public async Task CreateAsync_Throws_ArgumentNullException_When_Parent_Is_Missing() - { - var pagesCreateParameters = PagesCreateParametersBuilder.Create(null).Build(); + [Fact] + public async Task CreateAsync_Throws_ArgumentNullException_When_Parameter_Is_Null() + { + Func act = async () => await _client.CreateAsync(null); + + (await act.Should().ThrowAsync()).And.ParamName.Should().Be("pagesCreateParameters"); + } + + [Fact] + public async Task CreateAsync_Throws_ArgumentNullException_When_Parent_Is_Missing() + { + var pagesCreateParameters = PagesCreateParametersBuilder.Create(null).Build(); - Func act = async () => await _client.CreateAsync(pagesCreateParameters); + Func act = async () => await _client.CreateAsync(pagesCreateParameters); - (await act.Should().ThrowAsync()).And.ParamName.Should().Be("Parent"); - } + (await act.Should().ThrowAsync()).And.ParamName.Should().Be("Parent"); + } - [Fact] - public async Task CreateAsync_Throws_ArgumentNullException_When_Properties_Is_Missing() + [Fact] + public async Task CreateAsync_Throws_ArgumentNullException_When_Properties_Is_Missing() + { + var pagesCreateParameters = new PagesCreateParameters { - var pagesCreateParameters = new PagesCreateParameters - { - Parent = new ParentPageInput() - { - PageId = "3c357473-a281-49a4-88c0-10d2b245a589", - }, - Properties = null - }; + Parent = new ParentPageInput { PageId = "3c357473-a281-49a4-88c0-10d2b245a589" }, + Properties = null + }; - Func act = async () => await _client.CreateAsync(pagesCreateParameters); + Func act = async () => await _client.CreateAsync(pagesCreateParameters); - (await act.Should().ThrowAsync()).And.ParamName.Should().Be("Properties"); - } + (await act.Should().ThrowAsync()).And.ParamName.Should().Be("Properties"); } } diff --git a/Test/Notion.UnitTests/PropertyTests.cs b/Test/Notion.UnitTests/PropertyTests.cs index 57747568..55f6d1c1 100644 --- a/Test/Notion.UnitTests/PropertyTests.cs +++ b/Test/Notion.UnitTests/PropertyTests.cs @@ -1,105 +1,69 @@ using System; -using FluentAssertions; using Notion.Client; using Notion.Client.Extensions; using Xunit; -namespace Notion.UnitTests +namespace Notion.UnitTests; + +public class PropertyTests { - public class PropertyTests + [Theory] + [InlineData(typeof(CheckboxProperty), PropertyType.Checkbox)] + [InlineData(typeof(CreatedByProperty), PropertyType.CreatedBy)] + [InlineData(typeof(CreatedTimeProperty), PropertyType.CreatedTime)] + [InlineData(typeof(DateProperty), PropertyType.Date)] + [InlineData(typeof(EmailProperty), PropertyType.Email)] + [InlineData(typeof(FilesProperty), PropertyType.Files)] + [InlineData(typeof(FormulaProperty), PropertyType.Formula)] + [InlineData(typeof(LastEditedByProperty), PropertyType.LastEditedBy)] + [InlineData(typeof(LastEditedTimeProperty), PropertyType.LastEditedTime)] + [InlineData(typeof(NumberProperty), PropertyType.Number)] + [InlineData(typeof(PeopleProperty), PropertyType.People)] + [InlineData(typeof(PhoneNumberProperty), PropertyType.PhoneNumber)] + [InlineData(typeof(RelationProperty), PropertyType.Relation)] + [InlineData(typeof(RichTextProperty), PropertyType.RichText)] + [InlineData(typeof(RollupProperty), PropertyType.Rollup)] + [InlineData(typeof(SelectProperty), PropertyType.Select)] + [InlineData(typeof(TitleProperty), PropertyType.Title)] + [InlineData(typeof(UrlProperty), PropertyType.Url)] + [InlineData(typeof(UniqueIdProperty), PropertyType.UniqueId)] + [InlineData(typeof(ButtonProperty),PropertyType.Button)] + public void TestPropertyType(Type type, PropertyType expectedPropertyType) { - [Theory] - [InlineData(typeof(CheckboxProperty), PropertyType.Checkbox)] - [InlineData(typeof(CreatedByProperty), PropertyType.CreatedBy)] - [InlineData(typeof(CreatedTimeProperty), PropertyType.CreatedTime)] - [InlineData(typeof(DateProperty), PropertyType.Date)] - [InlineData(typeof(EmailProperty), PropertyType.Email)] - [InlineData(typeof(FilesProperty), PropertyType.Files)] - [InlineData(typeof(FormulaProperty), PropertyType.Formula)] - [InlineData(typeof(LastEditedByProperty), PropertyType.LastEditedBy)] - [InlineData(typeof(LastEditedTimeProperty), PropertyType.LastEditedTime)] - [InlineData(typeof(NumberProperty), PropertyType.Number)] - [InlineData(typeof(PeopleProperty), PropertyType.People)] - [InlineData(typeof(PhoneNumberProperty), PropertyType.PhoneNumber)] - [InlineData(typeof(RelationProperty), PropertyType.Relation)] - [InlineData(typeof(RichTextProperty), PropertyType.RichText)] - [InlineData(typeof(RollupProperty), PropertyType.Rollup)] - [InlineData(typeof(SelectProperty), PropertyType.Select)] - [InlineData(typeof(TitleProperty), PropertyType.Title)] - [InlineData(typeof(UrlProperty), PropertyType.Url)] - public void TestPropertyType(Type type, PropertyType expectedPropertyType) - { - var typeInstance = (Property)Activator.CreateInstance(type); - - var actualPropertyType = typeInstance.Type; + var typeInstance = (Property)Activator.CreateInstance(type); - Assert.Equal(expectedPropertyType, actualPropertyType); - } + var actualPropertyType = typeInstance!.Type; - [Theory] - [InlineData(typeof(CheckboxProperty), "checkbox")] - [InlineData(typeof(CreatedByProperty), "created_by")] - [InlineData(typeof(CreatedTimeProperty), "created_time")] - [InlineData(typeof(DateProperty), "date")] - [InlineData(typeof(EmailProperty), "email")] - [InlineData(typeof(FilesProperty), "files")] - [InlineData(typeof(FormulaProperty), "formula")] - [InlineData(typeof(LastEditedByProperty), "last_edited_by")] - [InlineData(typeof(LastEditedTimeProperty), "last_edited_time")] - [InlineData(typeof(NumberProperty), "number")] - [InlineData(typeof(PeopleProperty), "people")] - [InlineData(typeof(PhoneNumberProperty), "phone_number")] - [InlineData(typeof(RelationProperty), "relation")] - [InlineData(typeof(RichTextProperty), "rich_text")] - [InlineData(typeof(RollupProperty), "rollup")] - [InlineData(typeof(SelectProperty), "select")] - [InlineData(typeof(TitleProperty), "title")] - [InlineData(typeof(UrlProperty), "url")] - public void TestPropertyTypeText(Type type, string expectedPropertyType) - { - var typeInstance = (Property)Activator.CreateInstance(type); + Assert.Equal(expectedPropertyType, actualPropertyType); + } - var actualPropertyType = typeInstance.Type.GetEnumMemberValue(); + [Theory] + [InlineData(typeof(CheckboxProperty), "checkbox")] + [InlineData(typeof(CreatedByProperty), "created_by")] + [InlineData(typeof(CreatedTimeProperty), "created_time")] + [InlineData(typeof(DateProperty), "date")] + [InlineData(typeof(EmailProperty), "email")] + [InlineData(typeof(FilesProperty), "files")] + [InlineData(typeof(FormulaProperty), "formula")] + [InlineData(typeof(LastEditedByProperty), "last_edited_by")] + [InlineData(typeof(LastEditedTimeProperty), "last_edited_time")] + [InlineData(typeof(NumberProperty), "number")] + [InlineData(typeof(PeopleProperty), "people")] + [InlineData(typeof(PhoneNumberProperty), "phone_number")] + [InlineData(typeof(RelationProperty), "relation")] + [InlineData(typeof(RichTextProperty), "rich_text")] + [InlineData(typeof(RollupProperty), "rollup")] + [InlineData(typeof(SelectProperty), "select")] + [InlineData(typeof(TitleProperty), "title")] + [InlineData(typeof(UrlProperty), "url")] + [InlineData(typeof(UniqueIdProperty), "unique_id")] + [InlineData(typeof(ButtonProperty), "button")] + public void TestPropertyTypeText(Type type, string expectedPropertyType) + { + var typeInstance = (Property)Activator.CreateInstance(type); - Assert.Equal(expectedPropertyType, actualPropertyType); - } + var actualPropertyType = typeInstance!.Type.GetEnumMemberValue(); - [Theory] - [InlineData(null, NumberFormat.Unknown)] - [InlineData("number", NumberFormat.Number)] - [InlineData("number_with_commas", NumberFormat.NumberWithCommas)] - [InlineData("percent", NumberFormat.Percent)] - [InlineData("dollar", NumberFormat.Dollar)] - [InlineData("euro", NumberFormat.Euro)] - [InlineData("pound", NumberFormat.Pound)] - [InlineData("yen", NumberFormat.Yen)] - [InlineData("ruble", NumberFormat.Ruble)] - [InlineData("rupee", NumberFormat.Rupee)] - [InlineData("won", NumberFormat.Won)] - [InlineData("yuan", NumberFormat.Yuan)] - [InlineData("hong_kong_dollar", NumberFormat.HongKongDollar)] - [InlineData("new_zealand_dollar", NumberFormat.NewZealandDollar)] - [InlineData("krona", NumberFormat.Krona)] - [InlineData("norwegian_krone", NumberFormat.NorwegianKrone)] - [InlineData("mexican_peso", NumberFormat.MexicanPeso)] - [InlineData("rand", NumberFormat.Rand)] - [InlineData("new_taiwan_dollar", NumberFormat.NewTaiwanDollar)] - [InlineData("danish_krone", NumberFormat.DanishKrone)] - [InlineData("zloty", NumberFormat.Zloty)] - [InlineData("baht", NumberFormat.Baht)] - [InlineData("forint", NumberFormat.Forint)] - [InlineData("koruna", NumberFormat.Koruna)] - [InlineData("shekel", NumberFormat.Shekel)] - [InlineData("chilean_peso", NumberFormat.ChileanPeso)] - [InlineData("philippine_peso", NumberFormat.PhilippinePeso)] - [InlineData("dirham", NumberFormat.Dirham)] - [InlineData("colombian_peso", NumberFormat.ColombianPeso)] - [InlineData("riyal", NumberFormat.Riyal)] - [InlineData("ringgit", NumberFormat.Ringgit)] - [InlineData("leu", NumberFormat.Leu)] - public void NumberFormatEnumTypes(string textValue, NumberFormat numberFormat) - { - numberFormat.GetEnumMemberValue().Should().Be(textValue); - } + Assert.Equal(expectedPropertyType, actualPropertyType); } } diff --git a/Test/Notion.UnitTests/SearchClientTest.cs b/Test/Notion.UnitTests/SearchClientTest.cs index aa506572..a5a477cb 100644 --- a/Test/Notion.UnitTests/SearchClientTest.cs +++ b/Test/Notion.UnitTests/SearchClientTest.cs @@ -1,71 +1,69 @@ -using System; -using System.IO; +using System.IO; using System.Threading.Tasks; using FluentAssertions; using Notion.Client; using WireMock.ResponseBuilders; using Xunit; -namespace Notion.UnitTests +namespace Notion.UnitTests; + +public class SearchClientTest : ApiTestBase { - public class SearchClientTest : ApiTestBase - { - private readonly SearchClient _client; + private readonly SearchClient _client; - public SearchClientTest() - { - _client = new SearchClient(new RestClient(ClientOptions)); - } + public SearchClientTest() + { + _client = new SearchClient(new RestClient(ClientOptions)); + } - [Fact] - public async Task Search() - { - // Arrange - var path = ApiEndpoints.SearchApiUrls.Search(); - var jsonData = await File.ReadAllTextAsync("data/search/SearchResponse.json"); + [Fact] + public async Task Search() + { + // Arrange + var path = ApiEndpoints.SearchApiUrls.Search(); + var jsonData = await File.ReadAllTextAsync("data/search/SearchResponse.json"); - Server.Given(CreatePostRequestBuilder(path)) - .RespondWith( + Server.Given(CreatePostRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - SearchParameters searchParameters = new SearchParameters() + var searchParameters = new SearchRequest + { + Query = "External tasks", + Sort = new SearchSort { - Query = "External tasks", - Sort = new SearchSort - { - Direction = SearchDirection.Ascending, - Timestamp = "last_edited_time" - } - }; + Direction = SearchDirection.Ascending, + Timestamp = "last_edited_time" + } + }; - // Act - var searchResult = await _client.SearchAsync(searchParameters); + // Act + var searchResult = await _client.SearchAsync(searchParameters); - // Assert - var results = searchResult.Results; + // Assert + var results = searchResult.Results; - results.Should().SatisfyRespectively( - obj => - { - obj.Object.Should().Be(ObjectType.Database); + results.Should().SatisfyRespectively( + obj => + { + obj.Object.Should().Be(ObjectType.Database); - var database = (Database)obj; - database.Properties.Should().HaveCount(2); - }, - obj => - { - obj.Object.Should().Be(ObjectType.Page); + var database = (Database)obj; + database.Properties.Should().HaveCount(2); + }, + obj => + { + obj.Object.Should().Be(ObjectType.Page); - var page = (Page)obj; - page.IsArchived.Should().BeFalse(); - page.Properties.Should().HaveCount(1); - page.Parent.Should().BeAssignableTo(); - ((DatabaseParent)page.Parent).DatabaseId.Should().Be("e6c6f8ff-c70e-4970-91ba-98f03e0d7fc6"); - } - ); - } + var page = (Page)obj; + page.InTrash.Should().BeFalse(); + page.Properties.Should().HaveCount(1); + page.Parent.Should().BeAssignableTo(); + ((DatabaseParent)page.Parent).DatabaseId.Should().Be("e6c6f8ff-c70e-4970-91ba-98f03e0d7fc6"); + } + ); } } diff --git a/Test/Notion.UnitTests/UserClientTest.cs b/Test/Notion.UnitTests/UserClientTest.cs index d9b27984..6e93decc 100644 --- a/Test/Notion.UnitTests/UserClientTest.cs +++ b/Test/Notion.UnitTests/UserClientTest.cs @@ -5,145 +5,144 @@ using WireMock.ResponseBuilders; using Xunit; -namespace Notion.UnitTests +namespace Notion.UnitTests; + +public class UserClientTest : ApiTestBase { - public class UserClientTest : ApiTestBase + private readonly IUsersClient _client; + + public UserClientTest() + { + _client = new UsersClient(new RestClient(ClientOptions)); + } + + [Fact] + public async Task ListUsers() + { + // Arrange + var jsonData = await File.ReadAllTextAsync("data/users/ListUsersResponse.json"); + var path = ApiEndpoints.UsersApiUrls.List(); + + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( + Response.Create() + .WithStatusCode(200) + .WithBody(jsonData) + ); + + // Act + var users = await _client.ListAsync(); + + // Assert + users.Results.Should().SatisfyRespectively( + user => + { + user.Id.Should().Be("5e37c1b4-630f-4e81-bd6b-296af31e345f"); + user.Name.Should().Be("Vedant Koditkar"); + user.Type.Should().Be("person"); + user.Person.Email.Should().Be("vedkoditkar@gmail.com"); + user.Bot.Should().BeNull(); + }, + user => + { + user.Id.Should().Be("590693f3-797f-4970-98ff-7284106393e5"); + user.Name.Should().Be("Test"); + user.Type.Should().Be("bot"); + user.Person.Should().BeNull(); + } + ); + } + + [Fact] + public async Task RetrieveUser() { - private readonly IUsersClient _client; - - public UserClientTest() - { - _client = new UsersClient(new RestClient(ClientOptions)); - } - - [Fact] - public async Task ListUsers() - { - // Arrange - var jsonData = await File.ReadAllTextAsync("data/users/ListUsersResponse.json"); - var path = ApiEndpoints.UsersApiUrls.List(); - - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( + // Arrange + var jsonData = await File.ReadAllTextAsync("data/users/RetrieveUserResponse.json"); + var userId = "5e37c1b4-630f-4e81-bd6b-296af31e345f"; + var path = ApiEndpoints.UsersApiUrls.Retrieve(userId); + + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - // Act - var users = await _client.ListAsync(); - - // Assert - users.Results.Should().SatisfyRespectively( - user => - { - user.Id.Should().Be("5e37c1b4-630f-4e81-bd6b-296af31e345f"); - user.Name.Should().Be("Vedant Koditkar"); - user.Type.Should().Be("person"); - user.Person.Email.Should().Be("vedkoditkar@gmail.com"); - user.Bot.Should().BeNull(); - }, - user => - { - user.Id.Should().Be("590693f3-797f-4970-98ff-7284106393e5"); - user.Name.Should().Be("Test"); - user.Type.Should().Be("bot"); - user.Person.Should().BeNull(); - } + // Act + var user = await _client.RetrieveAsync(userId); + + // Assert + user.Id.Should().Be("5e37c1b4-630f-4e81-bd6b-296af31e345f"); + user.Name.Should().Be("Vedant Koditkar"); + user.Type.Should().Be("person"); + user.Person.Email.Should().Be("vedkoditkar@gmail.com"); + user.Bot.Should().BeNull(); + } + + [Fact] + public async Task RetrieveTokenUser_WorkspaceInternalToken() + { + // Arrange + var jsonData = await File.ReadAllTextAsync("data/users/MeResponse.json"); + + var path = ApiEndpoints.UsersApiUrls.Me(); + + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( + Response.Create() + .WithStatusCode(200) + .WithBody(jsonData) ); - } - - [Fact] - public async Task RetrieveUser() - { - // Arrange - var jsonData = await File.ReadAllTextAsync("data/users/RetrieveUserResponse.json"); - var userId = "5e37c1b4-630f-4e81-bd6b-296af31e345f"; - var path = ApiEndpoints.UsersApiUrls.Retrieve(userId); - - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( + + // Act + var user = await _client.MeAsync(); + + // Assert + user.Id.Should().Be("590693f3-797f-4970-98ff-7284106393e5"); + user.Name.Should().Be("Test"); + user.AvatarUrl.Should().BeNull(); + user.Type.Should().Be("bot"); + user.Person.Should().BeNull(); + user.Bot.Should().NotBeNull(); + user.Bot.Owner.Should().BeOfType(); + + var owner = (WorkspaceIntegrationOwner)user.Bot.Owner; + owner.Workspace.Should().BeTrue(); + } + + [Fact] + public async Task RetrieveTokenUser_UserLevelToken() + { + // Arrange + var jsonData = await File.ReadAllTextAsync("data/users/MeUserLevelResponse.json"); + + var path = ApiEndpoints.UsersApiUrls.Me(); + + Server.Given(CreateGetRequestBuilder(path)) + .RespondWith( Response.Create() .WithStatusCode(200) .WithBody(jsonData) ); - // Act - var user = await _client.RetrieveAsync(userId); - - // Assert - user.Id.Should().Be("5e37c1b4-630f-4e81-bd6b-296af31e345f"); - user.Name.Should().Be("Vedant Koditkar"); - user.Type.Should().Be("person"); - user.Person.Email.Should().Be("vedkoditkar@gmail.com"); - user.Bot.Should().BeNull(); - } - - [Fact] - public async Task RetrieveTokenUser_WorkspaceInternalToken() - { - // Arrange - var jsonData = await File.ReadAllTextAsync("data/users/MeResponse.json"); - - var path = ApiEndpoints.UsersApiUrls.Me(); - - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( - Response.Create() - .WithStatusCode(200) - .WithBody(jsonData) - ); - - // Act - var user = await _client.MeAsync(); - - // Assert - user.Id.Should().Be("590693f3-797f-4970-98ff-7284106393e5"); - user.Name.Should().Be("Test"); - user.AvatarUrl.Should().BeNull(); - user.Type.Should().Be("bot"); - user.Person.Should().BeNull(); - user.Bot.Should().NotBeNull(); - user.Bot.Owner.Should().BeOfType(); - - var owner = (WorkspaceIntegrationOwner)user.Bot.Owner; - owner.Workspace.Should().BeTrue(); - } - - [Fact] - public async Task RetrieveTokenUser_UserLevelToken() - { - // Arrange - var jsonData = await File.ReadAllTextAsync("data/users/MeUserLevelResponse.json"); - - var path = ApiEndpoints.UsersApiUrls.Me(); - - Server.Given(CreateGetRequestBuilder(path)) - .RespondWith( - Response.Create() - .WithStatusCode(200) - .WithBody(jsonData) - ); - - // Act - var user = await _client.MeAsync(); - - // Assert - user.Id.Should().Be("16d84278-ab0e-484c-9bdd-b35da3bd8905"); - user.Name.Should().Be("pied piper"); - user.AvatarUrl.Should().BeNull(); - user.Type.Should().Be("bot"); - user.Person.Should().BeNull(); - user.Bot.Should().NotBeNull(); - user.Bot.Owner.Should().BeOfType(); - - var owner = (UserOwner)user.Bot.Owner; - owner.User.Id.Should().Be("5389a034-eb5c-47b5-8a9e-f79c99ef166c"); - owner.User.Name.Should().Be("christine makenotion"); - owner.User.AvatarUrl.Should().BeNull(); - owner.User.Type.Should().Be("person"); - owner.User.Person.Email.Should().Be("christine@makenotion.com"); - owner.User.Bot.Should().BeNull(); - } + // Act + var user = await _client.MeAsync(); + + // Assert + user.Id.Should().Be("16d84278-ab0e-484c-9bdd-b35da3bd8905"); + user.Name.Should().Be("pied piper"); + user.AvatarUrl.Should().BeNull(); + user.Type.Should().Be("bot"); + user.Person.Should().BeNull(); + user.Bot.Should().NotBeNull(); + user.Bot.Owner.Should().BeOfType(); + + var owner = (UserOwner)user.Bot.Owner; + owner.User.Id.Should().Be("5389a034-eb5c-47b5-8a9e-f79c99ef166c"); + owner.User.Name.Should().Be("christine makenotion"); + owner.User.AvatarUrl.Should().BeNull(); + owner.User.Type.Should().Be("person"); + owner.User.Person.Email.Should().Be("christine@makenotion.com"); + owner.User.Bot.Should().BeNull(); } } diff --git a/Test/Notion.UnitTests/data/blocks/AppendBlockChildrenResponse.json b/Test/Notion.UnitTests/data/blocks/AppendBlockChildrenResponse.json index 7cba0666..13966312 100644 --- a/Test/Notion.UnitTests/data/blocks/AppendBlockChildrenResponse.json +++ b/Test/Notion.UnitTests/data/blocks/AppendBlockChildrenResponse.json @@ -7,9 +7,10 @@ "created_time": "2021-03-16T16:31:00.000Z", "last_edited_time": "2021-03-16T16:32:00.000Z", "has_children": false, + "in_trash" : false, "type": "heading_2", "heading_2": { - "text": [ + "rich_text": [ { "type": "text", "text": { @@ -38,7 +39,7 @@ "has_children": false, "type": "paragraph", "paragraph": { - "text": [ + "rich_text": [ { "type": "text", "text": { diff --git a/Test/Notion.UnitTests/data/blocks/RetrieveBlockChildrenResponse.json b/Test/Notion.UnitTests/data/blocks/RetrieveBlockChildrenResponse.json index 6ab04fea..b16dfc35 100644 --- a/Test/Notion.UnitTests/data/blocks/RetrieveBlockChildrenResponse.json +++ b/Test/Notion.UnitTests/data/blocks/RetrieveBlockChildrenResponse.json @@ -7,10 +7,10 @@ "created_time": "2021-05-29T16:22:00.000Z", "last_edited_time": "2021-05-29T16:22:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "paragraph", "paragraph": { - "text": [] + "rich_text": [] } }, { @@ -19,10 +19,10 @@ "created_time": "2021-05-30T09:25:00.000Z", "last_edited_time": "2021-05-30T09:25:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "heading_2", "heading_2": { - "text": [ + "rich_text": [ { "type": "text", "text": { @@ -49,10 +49,10 @@ "created_time": "2021-05-30T09:36:00.000Z", "last_edited_time": "2021-05-30T09:36:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "heading_2", "heading_2": { - "text": [ + "rich_text": [ { "type": "text", "text": { @@ -79,10 +79,10 @@ "created_time": "2021-08-18T21:12:00.000Z", "last_edited_time": "2021-08-18T21:12:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "paragraph", "paragraph": { - "text": [] + "rich_text": [] } }, { @@ -91,7 +91,7 @@ "created_time": "2021-08-18T23:00:00.000Z", "last_edited_time": "2021-08-18T23:00:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "child_page", "child_page": { "title": "" @@ -103,7 +103,7 @@ "created_time": "2021-08-18T23:00:00.000Z", "last_edited_time": "2021-08-18T23:00:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "child_page", "child_page": { "title": "" @@ -115,11 +115,73 @@ "created_time": "2021-09-09T05:22:00.000Z", "last_edited_time": "2021-09-09T05:22:00.000Z", "has_children": false, - "archived": false, + "in_trash": false, "type": "paragraph", "paragraph": { - "text": [] + "rich_text": [] } + }, + { + "object": "block", + "id": "ABD0E698-ABE6-42F2-80B1-24497AAE16F1", + "created_time": "2022-05-03T13:28:00.000Z", + "last_edited_time": "2022-05-03T13:29:00.000Z", + "has_children": false, + "in_trash": false, + "type": "image", + "image": { + "caption": [ + { + "type": "text", + "text": { + "content": "caption text", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "caption text", + "href": null + } + ], + "type": "file", + "file": { + "url": "https://s3.us-west-2.amazonaws.com/secure.notion-static.com/xxx", + "expiry_time": "2022-05-11T17:55:32.613Z" + } + } + }, + { + "object": "block", + "id": "570d1df0-56c9-42f7-b8b8-5315323f82de", + "parent": { + "type": "page_id", + "page_id": "9b02f058-bd87-46c2-95d2-7496fb9075bb" + }, + "created_time": "2023-02-03T13:59:00.000Z", + "last_edited_time": "2023-02-03T13:59:00.000Z", + "created_by": { + "object": "user", + "id": "92e803ec-193c-4bcd-b28e-88365858d562" + }, + "last_edited_by": { + "object": "user", + "id": "92e803ec-193c-4bcd-b28e-88365858d562" + }, + "has_children": true, + "in_trash": false, + "type": "synced_block", + "synced_block": { + "synced_from": { + "type": "block_id", + "block_id": "d2f7e3f3-c553-410d-a5f9-dfc0c335b169" + } + } } ], "next_cursor": null, diff --git a/Test/Notion.UnitTests/data/blocks/RetrieveBlockResponse.json b/Test/Notion.UnitTests/data/blocks/RetrieveBlockResponse.json index 201a2a96..6df6ddb2 100644 --- a/Test/Notion.UnitTests/data/blocks/RetrieveBlockResponse.json +++ b/Test/Notion.UnitTests/data/blocks/RetrieveBlockResponse.json @@ -4,9 +4,10 @@ "created_time": "2021-03-16T16:31:00.000Z", "last_edited_time": "2021-03-16T16:32:00.000Z", "has_children": false, + "in_trash" : false, "type": "to_do", "to_do": { - "text": [ + "rich_text": [ { "type": "text", "text": { diff --git a/Test/Notion.UnitTests/data/blocks/UpdateBlockResponse.json b/Test/Notion.UnitTests/data/blocks/UpdateBlockResponse.json index c9fb6a9a..c2d3a2c4 100644 --- a/Test/Notion.UnitTests/data/blocks/UpdateBlockResponse.json +++ b/Test/Notion.UnitTests/data/blocks/UpdateBlockResponse.json @@ -4,9 +4,10 @@ "created_time": "2021-03-16T16:31:00.000Z", "last_edited_time": "2021-03-16T16:32:00.000Z", "has_children": false, + "in_trash" : false, "type": "to_do", "to_do": { - "text": [ + "rich_text": [ { "type": "text", "text": { diff --git a/Test/Notion.UnitTests/data/databases/DatabasePropertyObjectContainNameProperty.json b/Test/Notion.UnitTests/data/databases/DatabasePropertyObjectContainNameProperty.json index 5ed09bb1..2951f5f3 100644 --- a/Test/Notion.UnitTests/data/databases/DatabasePropertyObjectContainNameProperty.json +++ b/Test/Notion.UnitTests/data/databases/DatabasePropertyObjectContainNameProperty.json @@ -71,8 +71,11 @@ "type": "relation", "relation": { "database_id": "f86f2262-0751-40f2-8f63-e3f7a3c39fcb", - "synced_property_name": "Related to sample table (Property)", - "synced_property_id": "VQ}{" + "type": "dual_property", + "dual_property": { + "synced_property_name": "Related to sample table (Property)", + "synced_property_id": "VQ}{" + } } }, "Name": { diff --git a/Test/Notion.UnitTests/data/databases/DatabasePropertyObjectContainParentProperty.json b/Test/Notion.UnitTests/data/databases/DatabasePropertyObjectContainParentProperty.json index 5ed09bb1..2951f5f3 100644 --- a/Test/Notion.UnitTests/data/databases/DatabasePropertyObjectContainParentProperty.json +++ b/Test/Notion.UnitTests/data/databases/DatabasePropertyObjectContainParentProperty.json @@ -71,8 +71,11 @@ "type": "relation", "relation": { "database_id": "f86f2262-0751-40f2-8f63-e3f7a3c39fcb", - "synced_property_name": "Related to sample table (Property)", - "synced_property_id": "VQ}{" + "type": "dual_property", + "dual_property": { + "synced_property_name": "Related to sample table (Property)", + "synced_property_id": "VQ}{" + } } }, "Name": { diff --git a/Test/Notion.UnitTests/data/databases/DatabasePropertyObjectContainRelation.json b/Test/Notion.UnitTests/data/databases/DatabasePropertyObjectContainRelation.json new file mode 100644 index 00000000..2951f5f3 --- /dev/null +++ b/Test/Notion.UnitTests/data/databases/DatabasePropertyObjectContainRelation.json @@ -0,0 +1,92 @@ +{ + "object": "database", + "id": "f0212efc-caf6-4afc-87f6-1c06f1dfc8a1", + "created_time": "2021-05-22T18:44:00.000Z", + "last_edited_time": "2021-05-23T12:29:00.000Z", + "title": [ + { + "type": "text", + "text": { + "content": "sample table", + "link": null + }, + "annotations": { + "bold": false, + "italic": false, + "strikethrough": false, + "underline": false, + "code": false, + "color": "default" + }, + "plain_text": "sample table", + "href": null + } + ], + "properties": { + "Tags": { + "id": "YG~h", + "name": "Tags", + "type": "multi_select", + "multi_select": { + "options": [] + } + }, + "SimpleText": { + "id": "_Dfp", + "name": "SimpleText", + "type": "rich_text", + "rich_text": {} + }, + "Column": { + "id": "bxhl", + "name": "Column", + "type": "multi_select", + "multi_select": { + "options": [ + { + "id": "5a44a233-33be-435e-b358-2c0ed1799dcf", + "name": "what", + "color": "gray" + } + ] + } + }, + "SelectProp": { + "id": "eZ[y", + "name": "SelectProp", + "type": "select", + "select": { + "options": [ + { + "id": "362dc255-c867-4543-b3ea-7bd988638228", + "name": "Female", + "color": "green" + } + ] + } + }, + "Property": { + "id": "zDGa", + "name": "Property", + "type": "relation", + "relation": { + "database_id": "f86f2262-0751-40f2-8f63-e3f7a3c39fcb", + "type": "dual_property", + "dual_property": { + "synced_property_name": "Related to sample table (Property)", + "synced_property_id": "VQ}{" + } + } + }, + "Name": { + "id": "title", + "name": "Name", + "type": "title", + "title": {} + } + }, + "parent": { + "type": "page_id", + "page_id": "649089db-8984-4051-98fb-a03593b852d8" + } +} diff --git a/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json b/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json index ef374f2d..9e1acfb0 100644 --- a/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json +++ b/Test/Notion.UnitTests/data/databases/DatabaseRetrieveResponse.json @@ -33,6 +33,7 @@ } }, "url": "https://www.notion.so/668d797c76fa49349b05ad288df2d136", + "in_trash" : false, "properties": { "Tags": { "id": "YG~h", @@ -82,10 +83,19 @@ "type": "relation", "relation": { "database_id": "f86f2262-0751-40f2-8f63-e3f7a3c39fcb", - "synced_property_name": "Related to sample table (Property)", - "synced_property_id": "VQ}{" + "type": "dual_property", + "dual_property": { + "synced_property_name": "Related to sample table (Property)", + "synced_property_id": "VQ}{" + } } }, + "SimpleButton": { + "id":"_ri%7C", + "name":"SimpleButton", + "type":"button", + "button": {} + }, "Name": { "id": "title", "name": "Name", diff --git a/Test/Notion.UnitTests/data/databases/DatabasesListResponse.json b/Test/Notion.UnitTests/data/databases/DatabasesListResponse.json index ea380375..ad0e5497 100644 --- a/Test/Notion.UnitTests/data/databases/DatabasesListResponse.json +++ b/Test/Notion.UnitTests/data/databases/DatabasesListResponse.json @@ -74,10 +74,19 @@ "type": "relation", "relation": { "database_id": "f86f2262-0751-40f2-8f63-e3f7a3c39fcb", - "synced_property_name": "Related to sample table (Property)", - "synced_property_id": "VQ}{" + "type": "dual_property", + "dual_property": { + "synced_property_name": "Related to sample table (Property)", + "synced_property_id": "VQ}{" + } } }, + "SimpleButton": { + "id":"_ri%7C", + "name":"SimpleButton", + "type":"button", + "button": {} + }, "Name": { "id": "title", "name": "Name", @@ -188,8 +197,11 @@ "type": "relation", "relation": { "database_id": "f0212efc-caf6-4afc-87f6-1c06f1dfc8a1", - "synced_property_name": "Property", - "synced_property_id": "zDGa" + "type": "dual_property", + "dual_property": { + "synced_property_name": "Property", + "synced_property_id": "zDGa" + } } }, "Column 1": { diff --git a/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json b/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json index 27e70234..d291a5b2 100644 --- a/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json +++ b/Test/Notion.UnitTests/data/databases/DatabasesQueryResponse.json @@ -10,7 +10,7 @@ "type": "database_id", "database_id": "897e5a76-ae52-4b48-9fdf-e71f5945d1af" }, - "archived": false, + "in_trash": false, "url": "https://www.notion.so/2e01e904febd43a0ad028eedb903a82c", "properties": { "Recipes": { @@ -45,6 +45,11 @@ "id": "{>U;", "type": "checkbox", "checkbox": false + }, + "Add Page Button": { + "id":"_ri%7C", + "type":"button", + "button": {} } } } diff --git a/Test/Notion.UnitTests/data/databases/Fix123QueryAsyncDateFormulaValueReturnsNullResponse.json b/Test/Notion.UnitTests/data/databases/Fix123QueryAsyncDateFormulaValueReturnsNullResponse.json index bf54bf0b..00159d76 100644 --- a/Test/Notion.UnitTests/data/databases/Fix123QueryAsyncDateFormulaValueReturnsNullResponse.json +++ b/Test/Notion.UnitTests/data/databases/Fix123QueryAsyncDateFormulaValueReturnsNullResponse.json @@ -12,7 +12,7 @@ "type": "database_id", "database_id": "f86f2262-0751-40f2-8f63-e3f7a3c39fcb" }, - "archived": false, + "in_trash": false, "properties": { "Column": { "id": "B[\\E", @@ -36,7 +36,7 @@ } }, "FormulaProp": { - "id": "JwY^", + "id": "JwY", "type": "formula", "formula": { "type": "date", diff --git a/Test/Notion.UnitTests/data/pages/CreatePageResponse.json b/Test/Notion.UnitTests/data/pages/CreatePageResponse.json index 89f0e58e..a801949b 100644 --- a/Test/Notion.UnitTests/data/pages/CreatePageResponse.json +++ b/Test/Notion.UnitTests/data/pages/CreatePageResponse.json @@ -3,7 +3,7 @@ "id": "251d2b5f-268c-4de2-afe9-c71ff92ca95c", "created_time": "2020-03-17T19:10:04.968Z", "last_edited_time": "2020-03-17T21:49:37.913Z", - "archived": false, + "in_trash": false, "url": "https://www.notion.so/Test-251d2b5f268c4de2afe9c71ff92ca95c", "properties": { "Name": { diff --git a/Test/Notion.UnitTests/data/pages/PageObjectShouldHaveUrlPropertyResponse.json b/Test/Notion.UnitTests/data/pages/PageObjectShouldHaveUrlPropertyResponse.json index 8f9d45ab..bf5ba373 100644 --- a/Test/Notion.UnitTests/data/pages/PageObjectShouldHaveUrlPropertyResponse.json +++ b/Test/Notion.UnitTests/data/pages/PageObjectShouldHaveUrlPropertyResponse.json @@ -7,7 +7,7 @@ "type": "database_id", "database_id": "48f8fee9-cd79-4180-bc2f-ec0398253067" }, - "archived": false, + "in_trash": false, "url": "https://www.notion.so/Avocado-251d2b5f268c4de2afe9c71ff92ca95c", "properties": { "Name": { diff --git a/Test/Notion.UnitTests/data/pages/ArchivePageResponse.json b/Test/Notion.UnitTests/data/pages/TrashPageResponse.json similarity index 87% rename from Test/Notion.UnitTests/data/pages/ArchivePageResponse.json rename to Test/Notion.UnitTests/data/pages/TrashPageResponse.json index 2f9af7c6..add1f546 100644 --- a/Test/Notion.UnitTests/data/pages/ArchivePageResponse.json +++ b/Test/Notion.UnitTests/data/pages/TrashPageResponse.json @@ -3,7 +3,7 @@ "id": "251d2b5f-268c-4de2-afe9-c71ff92ca95c", "created_time": "2020-03-17T19:10:04.968Z", "last_edited_time": "2020-03-17T21:49:37.913Z", - "archived": true, + "in_trash": true, "url": "https://www.notion.so/Test-251d2b5f268c4de2afe9c71ff92ca95c", "properties": { "In stock": { @@ -11,6 +11,11 @@ "type": "checkbox", "checkbox": true }, + "Add Page Button": { + "id":"_ri%7C", + "type":"button", + "button": {} + }, "Name": { "id": "title", "type": "title", diff --git a/Test/Notion.UnitTests/data/pages/UpdatePagePropertiesResponse.json b/Test/Notion.UnitTests/data/pages/UpdatePagePropertiesResponse.json index 12076368..70cc2164 100644 --- a/Test/Notion.UnitTests/data/pages/UpdatePagePropertiesResponse.json +++ b/Test/Notion.UnitTests/data/pages/UpdatePagePropertiesResponse.json @@ -3,7 +3,7 @@ "id": "251d2b5f-268c-4de2-afe9-c71ff92ca95c", "created_time": "2020-03-17T19:10:04.968Z", "last_edited_time": "2020-03-17T21:49:37.913Z", - "archived": false, + "in_trash": false, "url": "https://www.notion.so/Test-251d2b5f268c4de2afe9c71ff92ca95c", "properties": { "In stock": { diff --git a/Test/Notion.UnitTests/data/search/SearchResponse.json b/Test/Notion.UnitTests/data/search/SearchResponse.json index 107adcb3..53defa57 100644 --- a/Test/Notion.UnitTests/data/search/SearchResponse.json +++ b/Test/Notion.UnitTests/data/search/SearchResponse.json @@ -4,6 +4,7 @@ "object": "list", "results": [ { + "in_trash": false, "created_time": "2021-04-22T22:23:26.080Z", "id": "e6c6f8ff-c70e-4970-91ba-98f03e0d7fc6", "last_edited_time": "2021-04-23T04:21:00.000Z", @@ -43,7 +44,7 @@ ] }, { - "archived": false, + "in_trash": false, "created_time": "2021-04-23T04:21:00.000Z", "id": "4f555b50-3a9b-49cb-924c-3746f4ca5522", "last_edited_time": "2021-04-23T04:21:00.000Z", diff --git a/docs/README.md b/docs/README.md index 5ece487d..54a041d2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,8 +10,6 @@ A simple and easy to use client for the [Notion API](https://developers.notion.c dotnet add package Notion.Net ``` -> Note: From Nuget 2.0.0 notion client sdk default sets the Notion-Version header to 2021-08-16. - ## Usage > Before getting started, you need to [create an integration](https://www.notion.com/my-integrations) and find the token. You can learn more about authorization [here](https://developers.notion.com/docs/authorization). @@ -80,7 +78,6 @@ var complexFiler = new CompoundFilter( - [x] Create a database - [x] Update database - [x] Retrieve a database - - [x] List databases (Deprecated: use Search API instead) - [x] Pages - [x] Retrieve a page - [x] Create a page @@ -92,6 +89,9 @@ var complexFiler = new CompoundFilter( - [x] Retrieve block children - [x] Append block children - [x] Delete a block +- [x] Comments + - [x] Retrieve comments + - [x] Create comment - [x] Users - [x] Retrieve a User - [x] List all users diff --git a/examples/aspnet-core-app/aspnet-core-app.csproj b/examples/aspnet-core-app/aspnet-core-app.csproj index ecb2e709..8310dd1b 100644 --- a/examples/aspnet-core-app/aspnet-core-app.csproj +++ b/examples/aspnet-core-app/aspnet-core-app.csproj @@ -1,10 +1,10 @@ - net5.0 + net6.0 aspnet_core_app - \ No newline at end of file + diff --git a/examples/list-users/list-users.csproj b/examples/list-users/list-users.csproj index c3d3affe..22264436 100644 --- a/examples/list-users/list-users.csproj +++ b/examples/list-users/list-users.csproj @@ -1,7 +1,7 @@ Exe - net5.0 + net6.0 list_users diff --git a/package.json b/package.json new file mode 100644 index 00000000..a32bb1db --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "notion-sdk-net", + "version": "1.0.0", + "main": "index.js", + "repository": "git@github.com:notion-dotnet/notion-sdk-net.git", + "author": "Vedant Koditkar", + "license": "MIT", + "scripts": { + "lint": "dotnet jb cleanupcode --include=\"**/**.cs\" .\\Notion.sln" + } +}