├── .babelrc
├── .eslintignore
├── .eslintrc
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── config.yml
│ ├── feature_request.md
│ └── question_help.md
├── dependabot.yml
├── pull_request_template.md
└── workflows
│ ├── ci.yml
│ ├── coverage.yml
│ ├── documentation.yml
│ ├── npm_upload.yml
│ └── spec_update.yml
├── .gitignore
├── .gitmodules
├── .jsdoc.json
├── .nycrc.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── UPGRADING.md
├── codecov.yml
├── examples
├── README.md
├── javascript
│ ├── .eslintrc
│ ├── PKCE-backend
│ │ ├── code_flow_example.js
│ │ ├── package-lock.json
│ │ └── package.json
│ ├── auth
│ │ └── index.html
│ ├── basic
│ │ └── index.html
│ ├── download
│ │ └── index.html
│ ├── index.html
│ ├── node
│ │ ├── basic.js
│ │ ├── download.js
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── team-as-user.js
│ │ ├── team.js
│ │ └── upload.js
│ ├── pkce-browser
│ │ └── index.html
│ ├── server.js
│ ├── simple-backend
│ │ ├── code_flow_example.js
│ │ ├── package-lock.json
│ │ └── package.json
│ ├── styles.css
│ ├── team-as-user
│ │ └── index.html
│ ├── team
│ │ └── index.html
│ ├── upload
│ │ └── index.html
│ └── utils.js
└── typescript
│ ├── .eslintrc
│ ├── basic
│ ├── basic.js.map
│ ├── basic.ts
│ └── index.js
│ ├── download
│ ├── download.js.map
│ ├── download.ts
│ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── team-as-user
│ ├── index.js
│ ├── team-as-user.js.map
│ └── team-as-user.ts
│ ├── team
│ ├── index.js
│ ├── team.js.map
│ └── team.ts
│ ├── tsconfig.json
│ └── upload
│ ├── index.js
│ ├── upload.js.map
│ └── upload.ts
├── generator
├── generate_routes.py
└── typescript
│ ├── dropbox_types.d.tstemplate
│ └── index.d.tstemplate
├── index.js
├── lib
├── routes.js
└── types.js
├── package-lock.json
├── package.json
├── rollup.config.js
├── scripts
├── release_note_generator.sh
└── update_version.sh
├── src
├── auth.js
├── constants.js
├── dropbox.js
├── error.js
├── response.js
└── utils.js
├── test
├── .eslintrc
├── build
│ ├── browser.js
│ ├── browser
│ │ ├── es.html
│ │ ├── umd.html
│ │ └── umd.min.html
│ ├── node.js
│ └── node
│ │ ├── js_cjs
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ └── require.js
│ │ ├── js_esm
│ │ ├── import.js
│ │ ├── namedImport.js
│ │ ├── package-lock.json
│ │ └── package.json
│ │ ├── ts_cjs
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── require.js
│ │ ├── require.js.map
│ │ ├── require.ts
│ │ └── tsconfig.json
│ │ └── ts_es6
│ │ ├── import.js
│ │ ├── import.js.map
│ │ ├── import.ts
│ │ ├── namedImport.js
│ │ ├── namedImport.js.map
│ │ ├── namedImport.ts
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ └── tsconfig.json
├── fixtures
│ └── test.txt
├── integration
│ ├── team.js
│ └── user.js
├── types
│ ├── .eslintrc
│ ├── tsconfig.json
│ ├── types_test.js
│ ├── types_test.js.map
│ └── types_test.ts
└── unit
│ ├── auth.js
│ ├── dropbox.js
│ ├── error.js
│ ├── response.js
│ └── utils.js
└── types
├── .eslintrc
├── dropbox_types.d.ts
└── index.d.ts
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-env"],
3 | "env": {
4 | "coverage": {
5 | "plugins": ["istanbul"]
6 | },
7 | "commonjs": {
8 | "presets": [[
9 | "@babel/preset-env", {
10 | "modules": "cjs"
11 | }
12 | ]],
13 | "plugins": [
14 | ["transform-es2015-modules-commonjs", { "noInterop": true }]
15 | ]
16 | },
17 | "es": {
18 | "presets": [[
19 | "@babel/preset-env", {
20 | "modules": false
21 | }
22 | ]]
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | lib/types.js
2 | lib/routes.js
3 | types/dropbox_types.d.ts
4 | types/index.d.ts
5 | test/types/types_test.js
6 | test/types/types_test.js.map
7 | test/build/node/**
8 | docs/**
9 | dist/
10 | es/
11 | cjs/
12 | examples/typescript/**/*.js
13 | examples/typescript/**/*.js.map
14 | **/node_modules/**
15 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["airbnb-base"],
3 | "env": {
4 | "node": true
5 | },
6 | "rules": {
7 | "func-names": 0,
8 | "no-param-reassign": 0,
9 | "import/prefer-default-export": 0,
10 | "import/extensions": 0,
11 | "no-undef": 0
12 | },
13 | "parserOptions": {
14 | "ecmaVersion": 2017,
15 | "impliedStrict": false
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F41B Bug report"
3 | about: Create a report to help us improve the SDK
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of the bug.
12 |
13 | **To Reproduce**
14 | The steps to reproduce the behavior
15 |
16 | **Expected Behavior**
17 | A clear description of what you expected to happen.
18 |
19 | **Actual Behavior**
20 | A clear description of what actually happened
21 |
22 | **Screenshots**
23 | If applicable, add screenshots to help explain your problem.
24 |
25 | **Versions**
26 | * What version of the SDK are you using?
27 | * What version of the language are you using?
28 | * Are you using Javascript or Typescript?
29 | * What platform are you using? (if applicable)
30 |
31 | **Additional context**
32 | Add any other context about the problem here.
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F680 Feature Request"
3 | about: Suggest an idea for this SDK
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Why is this feature valuable to you? Does it solve a problem you're having?**
11 | A clear and concise description of why this feature is valuable. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered. (if applicable)
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question_help.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F4AC Questions / Help"
3 | about: Get help with issues you are experiencing
4 | title: ''
5 | labels: help-wanted, question
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Before you start**
11 | Have you checked StackOverflow, previous issues, and Dropbox Developer Forums for help?
12 |
13 | **What is your question?**
14 | A clear and concise description of the question.
15 |
16 | **Screenshots**
17 | If applicable, add screenshots to help explain your question.
18 |
19 | **Versions**
20 | * What version of the SDK are you using?
21 | * What version of the language are you using?
22 | * Are you using Javascript or Typescript?
23 | * What platform are you using? (if applicable)
24 |
25 | **Additional context**
26 | Add any other context about the question here.
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | # Updates to main package
9 | - package-ecosystem: "npm"
10 | directory: "/"
11 | schedule:
12 | interval: "monthly"
13 | # Updates to TS examples
14 | - package-ecosystem: "npm"
15 | directory: "examples/typescript"
16 | schedule:
17 | interval: "monthly"
18 |
19 | - package-ecosystem: "github-actions"
20 | directory: "/"
21 | schedule:
22 | interval: "monthly"
23 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | ## **Checklist**
6 |
7 |
8 | **General Contributing**
9 | - [ ] Have you read the Code of Conduct and signed the [CLA](https://opensource.dropbox.com/cla/)?
10 |
11 | **Is This a Code Change?**
12 | - [ ] Non-code related change (markdown/git settings etc)
13 | - [ ] SDK Code Change
14 | - [ ] Example/Test Code Change
15 |
16 | **Validation**
17 | - [ ] Does `npm test` pass?
18 | - [ ] Does `npm run build` pass?
19 | - [ ] Does `npm run lint` pass?
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on:
3 | pull_request:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | CI:
8 | continue-on-error: true
9 | runs-on: ${{ matrix.os }}
10 | strategy:
11 | matrix:
12 | os: [ubuntu-latest, macos-latest]
13 | node: [10, 11, 12, 13, 14]
14 | steps:
15 | - uses: actions/checkout@v2.3.4
16 | - name: Setup Node.js environment
17 | uses: actions/setup-node@v2.1.5
18 | with:
19 | node-version: ${{ matrix.node }}
20 | - name: Install SDK
21 | run: |
22 | npm install
23 | - name: Run Build
24 | run: |
25 | npm run build
26 | - name: Run Examples Builds
27 | run: |
28 | cd examples/typescript/
29 | npm install
30 | npm run build
31 | cd ../..
32 | - name: Run Linter
33 | run: |
34 | npm run lint
35 | - name: Run Unit Tests
36 | run: |
37 | npm run test:unit
38 | - name: Run TypeScipt Tests
39 | run: |
40 | npm run test:typescript
41 | - name: Run Build Tests
42 | if: matrix.node >= 12 # "type" = "module" was introduced in node v12 so lower versions will fail ESM tests
43 | run: |
44 | npm run clean
45 | npm run build
46 | npm run test:build
47 | Integration:
48 | runs-on: ${{ matrix.os }}
49 | strategy:
50 | matrix:
51 | os: [ubuntu-latest, macos-latest]
52 | node: [14] # Can only be ran on 14 because it relies on fs/promises
53 | steps:
54 | - uses: actions/checkout@v2.3.4
55 | - name: Setup Node.js environment
56 | uses: actions/setup-node@v2.1.5
57 | with:
58 | node-version: ${{ matrix.node }}
59 | - name: Install SDK
60 | run: |
61 | npm install
62 | - name: Run Integration Tests
63 | env:
64 | LEGACY_USER_DROPBOX_TOKEN: ${{ secrets.LEGACY_USER_DROPBOX_TOKEN }}
65 | LEGACY_USER_CLIENT_ID: ${{ secrets.LEGACY_USER_CLIENT_ID }}
66 | LEGACY_USER_CLIENT_SECRET: ${{ secrets.LEGACY_USER_CLIENT_SECRET }}
67 | LEGACY_USER_REFRESH_TOKEN: ${{ secrets.LEGACY_USER_REFRESH_TOKEN }}
68 | SCOPED_USER_DROPBOX_TOKEN: ${{ secrets.SCOPED_USER_DROPBOX_TOKEN }}
69 | SCOPED_USER_CLIENT_ID: ${{ secrets.SCOPED_USER_CLIENT_ID }}
70 | SCOPED_USER_CLIENT_SECRET: ${{ secrets.SCOPED_USER_CLIENT_SECRET }}
71 | SCOPED_USER_REFRESH_TOKEN: ${{ secrets.SCOPED_USER_REFRESH_TOKEN }}
72 | SCOPED_TEAM_DROPBOX_TOKEN: ${{ secrets.SCOPED_TEAM_DROPBOX_TOKEN }}
73 | SCOPED_TEAM_CLIENT_ID: ${{ secrets.SCOPED_TEAM_CLIENT_ID }}
74 | SCOPED_TEAM_CLIENT_SECRET: ${{ secrets.SCOPED_TEAM_CLIENT_SECRET }}
75 | SCOPED_TEAM_REFRESH_TOKEN: ${{ secrets.SCOPED_TEAM_REFRESH_TOKEN }}
76 | DROPBOX_SHARED_LINK: ${{ secrets.DROPBOX_SHARED_LINK }}
77 | run: |
78 | npm run test:integration
79 |
--------------------------------------------------------------------------------
/.github/workflows/coverage.yml:
--------------------------------------------------------------------------------
1 | name: CodeCov
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | schedule:
8 | - cron: 0 0 * * *
9 |
10 | jobs:
11 | Unit:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2.3.4
15 | - name: Setup Node.js environment
16 | uses: actions/setup-node@v2.1.5
17 | with:
18 | node-version: '14'
19 | - name: Install SDK
20 | run: |
21 | npm install
22 | - name: Generate Unit Test Coverage
23 | run: |
24 | npm run coverage:unit
25 | - name: Publish Coverage
26 | uses: codecov/codecov-action@v1.5.0
27 | with:
28 | flags: unit
29 | fail_ci_if_error: true
30 | Integration:
31 | runs-on: ubuntu-latest
32 | steps:
33 | - uses: actions/checkout@v2.3.4
34 | - name: Setup Node.js environment
35 | uses: actions/setup-node@v2.1.5
36 | with:
37 | node-version: '14'
38 | - name: Install SDK
39 | run: |
40 | npm install
41 | - name: Generate Integration Test Coverage
42 | env:
43 | LEGACY_USER_DROPBOX_TOKEN: ${{ secrets.LEGACY_USER_DROPBOX_TOKEN }}
44 | LEGACY_USER_CLIENT_ID: ${{ secrets.LEGACY_USER_CLIENT_ID }}
45 | LEGACY_USER_CLIENT_SECRET: ${{ secrets.LEGACY_USER_CLIENT_SECRET }}
46 | LEGACY_USER_REFRESH_TOKEN: ${{ secrets.LEGACY_USER_REFRESH_TOKEN }}
47 | SCOPED_USER_DROPBOX_TOKEN: ${{ secrets.SCOPED_USER_DROPBOX_TOKEN }}
48 | SCOPED_USER_CLIENT_ID: ${{ secrets.SCOPED_USER_CLIENT_ID }}
49 | SCOPED_USER_CLIENT_SECRET: ${{ secrets.SCOPED_USER_CLIENT_SECRET }}
50 | SCOPED_USER_REFRESH_TOKEN: ${{ secrets.SCOPED_USER_REFRESH_TOKEN }}
51 | SCOPED_TEAM_DROPBOX_TOKEN: ${{ secrets.SCOPED_TEAM_DROPBOX_TOKEN }}
52 | SCOPED_TEAM_CLIENT_ID: ${{ secrets.SCOPED_TEAM_CLIENT_ID }}
53 | SCOPED_TEAM_CLIENT_SECRET: ${{ secrets.SCOPED_TEAM_CLIENT_SECRET }}
54 | SCOPED_TEAM_REFRESH_TOKEN: ${{ secrets.SCOPED_TEAM_REFRESH_TOKEN }}
55 | DROPBOX_SHARED_LINK: ${{ secrets.DROPBOX_SHARED_LINK }}
56 | run: |
57 | npm run coverage:integration
58 | - name: Publish Coverage
59 | uses: codecov/codecov-action@v1.5.0
60 | with:
61 | flags: integration
62 | fail_ci_if_error: true
63 |
--------------------------------------------------------------------------------
/.github/workflows/documentation.yml:
--------------------------------------------------------------------------------
1 | name: Update Documentation
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | npm:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2.3.4
13 | - uses: actions/setup-node@v2.1.5
14 | with:
15 | registry-url: https://npm.pkg.github.com/
16 | node-version: 14
17 | - name: Build Package
18 | run: |
19 | npm install
20 | npm run build
21 | - name: Build Documentation
22 | run: npm run generate-docs
23 | - name: Push Documentation
24 | uses: JamesIves/github-pages-deploy-action@releases/v3
25 | with:
26 | ACCESS_TOKEN: ${{ secrets.GH_PAGES_PUBLISH_TOKEN }}
27 | BRANCH: gh-pages # The branch the action should deploy to.
28 | FOLDER: docs # The folder the action should deploy.
--------------------------------------------------------------------------------
/.github/workflows/npm_upload.yml:
--------------------------------------------------------------------------------
1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3 |
4 | name: Publish to NPM
5 |
6 | on:
7 | workflow_dispatch:
8 | release:
9 | types: [created]
10 |
11 | jobs:
12 | npm:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2.3.4
16 | - uses: actions/setup-node@v2.1.5
17 | with:
18 | node-version: 14
19 | - name: Build Package
20 | run: |
21 | npm install
22 | npm run build
23 | - name: NPM Publish
24 | uses: JS-DevTools/npm-publish@v1.4.3
25 | with:
26 | token: ${{ secrets.NPM_TOKEN }}
27 |
--------------------------------------------------------------------------------
/.github/workflows/spec_update.yml:
--------------------------------------------------------------------------------
1 | name: Spec Update
2 | on:
3 | workflow_dispatch:
4 | repository_dispatch:
5 | types: [spec_update]
6 |
7 | jobs:
8 | Update:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v2.3.4
12 | - name: Setup Python environment
13 | uses: actions/setup-python@v2.2.2
14 | with:
15 | python-version: 3.7
16 | - name: Setup Node.JS environment
17 | uses: actions/setup-node@v2.1.5
18 | with:
19 | node-version: 14
20 | - name: Get current time
21 | uses: 1466587594/get-current-time@v2
22 | id: current-time
23 | with:
24 | format: YYYY_MM_DD
25 | utcOffset: "-08:00"
26 | - name: Update SDK Version
27 | run: npm version --commit-hooks false --git-tag-version false minor
28 | - name: Install SDK
29 | run: |
30 | npm install
31 | - name: Update Modules
32 | run: |
33 | git submodule init
34 | git submodule update --remote --recursive
35 | - name: Generate Branch Name
36 | id: git-branch
37 | run: |
38 | echo "::set-output name=branch::spec_update_${{ steps.current-time.outputs.formattedTime }}"
39 | - name: Generate Num Diffs
40 | id: git-diff-num
41 | run: |
42 | cd generator/
43 | diffs=$(git diff --submodule dropbox-api-spec | grep ">" | wc -l)
44 | echo "Number of Spec diffs: $diffs"
45 | echo "::set-output name=num-diff::$diffs"
46 | cd ..
47 | - name: Generate Diff
48 | id: git-diff
49 | run: |
50 | cd generator/dropbox-api-spec
51 | gitdiff=$(git log -n ${{ steps.git-diff-num.outputs.num-diff }} --pretty="format:%n %H %n%n %b")
52 | commit="Automated Spec Update $gitdiff"
53 | commit="${commit//'%'/'%25'}"
54 | commit="${commit//$'\n'/'%0A'}"
55 | commit="${commit//$'\r'/'%0D'}"
56 | echo "Commit Message: $commit"
57 | echo "::set-output name=commit::$commit"
58 | cd ../..
59 | - name: Generate New Routes
60 | run: |
61 | cd generator/stone
62 | python setup.py install
63 | cd ..
64 | python generate_routes.py
65 | cd ..
66 | - name: Create Pull Request
67 | uses: peter-evans/create-pull-request@v3.9.1
68 | if: steps.git-diff-num.outputs.num-diff != 0
69 | with:
70 | token: ${{ secrets.SPEC_UPDATE_TOKEN }}
71 | commit-message: |
72 | ${{ steps.git-diff.outputs.commit}}
73 | branch: ${{ steps.git-branch.outputs.branch }}
74 | delete-branch: true
75 | title: 'Automated Spec Update'
76 | body: |
77 | ${{ steps.git-diff.outputs.commit}}
78 | base: 'main'
79 | team-reviewers: |
80 | owners
81 | maintainers
82 | draft: false
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # IDE config folders
2 | .idea/
3 |
4 | #Output Directories
5 | es/
6 | cjs/
7 | umd/
8 |
9 | # Logs
10 | logs
11 | *.log
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 | lerna-debug.log*
16 |
17 |
18 | docs/
19 |
20 | # Diagnostic reports (https://nodejs.org/api/report.html)
21 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
22 |
23 | # Runtime data
24 | pids
25 | *.pid
26 | *.seed
27 | *.pid.lock
28 |
29 | # Directory for instrumented libs generated by jscoverage/JSCover
30 | lib-cov
31 |
32 | # Coverage directory used by tools like istanbul
33 | coverage
34 | *.lcov
35 |
36 | # nyc test coverage
37 | .nyc_output
38 |
39 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
40 | .grunt
41 |
42 | # Bower dependency directory (https://bower.io/)
43 | bower_components
44 |
45 | # node-waf configuration
46 | .lock-wscript
47 |
48 | # Compiled binary addons (https://nodejs.org/api/addons.html)
49 | build/Release
50 |
51 | # Dependency directories
52 | node_modules/
53 | jspm_packages/
54 |
55 | # TypeScript v1 declaration files
56 | typings/
57 |
58 | # TypeScript cache
59 | *.tsbuildinfo
60 |
61 | # Optional npm cache directory
62 | .npm
63 |
64 | # Optional eslint cache
65 | .eslintcache
66 |
67 | # Microbundle cache
68 | .rpt2_cache/
69 | .rts2_cache_cjs/
70 | .rts2_cache_es/
71 | .rts2_cache_umd/
72 |
73 | # Optional REPL history
74 | .node_repl_history
75 |
76 | # Output of 'npm pack'
77 | *.tgz
78 |
79 | # Yarn Integrity file
80 | .yarn-integrity
81 |
82 | # dotenv environment variables file
83 | .env
84 | .env.test
85 |
86 | # parcel-bundler cache (https://parceljs.org/)
87 | .cache
88 |
89 | # Next.js build output
90 | .next
91 |
92 | # Nuxt.js build / generate output
93 | .nuxt
94 | dist
95 |
96 | # Gatsby files
97 | .cache/
98 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
99 | # https://nextjs.org/blog/next-9-1#public-directory-support
100 | # public
101 |
102 | # vuepress build output
103 | .vuepress/dist
104 |
105 | # Serverless directories
106 | .serverless/
107 |
108 | # FuseBox cache
109 | .fusebox/
110 |
111 | # DynamoDB Local files
112 | .dynamodb/
113 |
114 | # TernJS port file
115 | .tern-port
116 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "dropbox-api-spec"]
2 | path = generator/dropbox-api-spec
3 | url = https://github.com/dropbox/dropbox-api-spec.git
4 | [submodule "stone"]
5 | path = generator/stone
6 | url = https://github.com/dropbox/stone.git
7 |
--------------------------------------------------------------------------------
/.jsdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "source": {
3 | "include": ["./src", "./lib"]
4 | },
5 | "opts": {
6 | "template": "./node_modules/ink-docstrap/template",
7 | "destination": "docs",
8 | "readme": "./README.md",
9 | "recurse": true
10 | },
11 | "templates": {
12 | "systemName": "Dropbox Node SDK",
13 | "theme": "cosmo"
14 | }
15 | }
--------------------------------------------------------------------------------
/.nycrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "all": true,
3 | "include": [
4 | "src/*.js",
5 | "lib/*.js"
6 | ],
7 | "require": ["@babel/register"]
8 | }
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Dropbox Code Of Conduct
2 |
3 | *Dropbox believes that an inclusive development environment fosters greater technical achievement. To encourage a diverse group of contributors we've adopted this code of conduct.*
4 |
5 | Please read the Official Dropbox [Code of Conduct](https://opensource.dropbox.com/coc/) before contributing.
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to the Dropbox SDK for Javascript
2 | We value and rely on the feedback from our community. This comes in the form of bug reports, feature requests, and general guidance. We welcome your issues and pull requests and try our hardest to be timely in both response and resolution. Please read through this document before submitting issues or pull requests to ensure we have the necessary information to help you resolve your issue.
3 |
4 | ## Filing Bug Reports
5 | You can file a bug report on the [GitHub Issues][issues] page.
6 |
7 | 1. Search through existing issues to ensure that your issue has not been reported. If it is a common issue, there is likely already an issue.
8 |
9 | 2. Please ensure you are using the latest version of the SDK. While this may be a valid issue, we only will fix bugs affecting the latest version and your bug may have been fixed in a newer version.
10 |
11 | 3. Provide as much information as you can regarding the language version, SDK version, and any other relevant information about your environment so we can help resolve the issue as quickly as possible.
12 |
13 | ## Submitting Pull Requests
14 |
15 | We are more than happy to recieve pull requests helping us improve the state of our SDK. You can open a new pull request on the [GitHub Pull Requests][pr] page.
16 |
17 | 1. Please ensure that you have read the [License][license], [Code of Conduct][coc] and have signed the [Contributing License Agreement (CLA)][cla].
18 |
19 | 2. Please add tests confirming the new functionality works. Pull requests will not be merged without passing continuous integration tests unless the pull requests aims to fix existing issues with these tests.
20 |
21 | 3. If the pull request is modifying typescript definitions, please remember to change the template found under `generator/typescript` and run the generation instead of manually changing types. If there is an issue with the generation, please file an issue.
22 |
23 | ## Updating Generated Code
24 |
25 | Generated code can be updated by running the following commands:
26 |
27 | ```
28 | $ git submodule init
29 | $ git submodule update --remote --recursive
30 | $ cd generator/stone
31 | $ python setup.py install
32 | $ cd ..
33 | $ python generate_routes.py
34 | ```
35 |
36 | This will generate typescript definitions and route code.
37 |
38 | ## Testing the Code
39 |
40 | Tests live under the `test/` folder and are then broken down into the type of test it is. To run both the unit tests and the typescript tests, you can use:
41 |
42 | ```
43 | $ npm test
44 | ```
45 |
46 | If you would like to run the integration tests locally, you can run:
47 |
48 | ```
49 | export DROPBOX_TOKEN={fill in user token}
50 | export DROPBOX_TEAM_TOKEN={fill in team token}
51 | export DROPBOX_USER_ID={fill in assume user id}
52 | export DROPBOX_SHARED_LINK={fill in shared link}
53 | $ npm run test:integration
54 | ```
55 |
56 | Note: If you do not have all of these tokens available, we run integration tests as a part of pull request validation and you are able to rely on those if you are unable to obtain yourself.
57 |
58 | [issues]: https://github.com/dropbox/dropbox-sdk-js/issues
59 | [pr]: https://github.com/dropbox/dropbox-sdk-js/pulls
60 | [coc]: https://github.com/dropbox/dropbox-sdk-js/blob/main/CODE_OF_CONDUCT.md
61 | [license]: https://github.com/dropbox/dropbox-sdk-js/blob/main/LICENSE
62 | [cla]: https://opensource.dropbox.com/cla/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2020 Dropbox Inc., http://www.dropbox.com/
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [![Logo][logo]][repo]
2 |
3 | [](https://www.npmjs.com/package/dropbox)
4 | [](https://www.npmjs.com/package/dropbox)
5 | [](https://codecov.io/gh/dropbox/dropbox-sdk-js)
6 |
7 | The offical Dropbox SDK for Javascript.
8 |
9 | Documentation can be found on [GitHub Pages][documentation]
10 |
11 | ## Installation
12 |
13 | Create an app via the [Developer Console][devconsole]
14 |
15 | Install via [npm](https://www.npmjs.com/)
16 |
17 | ```
18 | $ npm install --save dropbox
19 | ```
20 |
21 | Install from source:
22 |
23 | ```
24 | $ git clone https://github.com/dropbox/dropbox-sdk-js.git
25 | $ cd dropbox-sdk-js
26 | $ npm install
27 | ```
28 |
29 | If you are using the repository from the browser, you can use any CDNs that hosts the Dropbox package by including a script tag with the link to the package. However, we highly recommend you do not directly import the latest version and instead choose a specific version. When we update and release a breaking change, this could break production code which we hope to avoid. Note, we follow [semver](https://semver.org/) naming conventions which means that any major version update could contain a breaking change.
30 |
31 | After installation, follow one of our [Examples][examples] or read the [Documentation][documentation].
32 |
33 | You can also view our [OAuth guide][oauthguide].
34 |
35 | ## Examples
36 |
37 | We provide [Examples][examples] to help get you started with a lot of the basic functionality in the SDK. We provide most examples in both Javascript and Typescript with some having a Node equivalent.
38 |
39 | - **OAuth**
40 | - Auth - [ [JS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/javascript/auth) ] - A simple auth example to get an access token and list the files in the root of your Dropbox account.
41 | - Simple Backend [ [JS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/javascript/simple-backend) ] - A simple example of a node backend doing a multi-step auth flow for Short Lived Tokens.
42 | - PKCE Backend [ [JS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/javascript/PKCE-backend) ] - A simple example of a node backend doing a multi-step auth flow using PKCE and Short Lived Tokens.
43 | - PKCE Browser [ [JS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/javascript/pkce-browser) ] - A simple example of a frontend doing a multi-step auth flow using PKCE and Short Lived Tokens.
44 |
45 | - **Other Examples**
46 | - Basic - [ [TS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/typescript/basic), [JS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/javascript/basic) ] - A simple example that takes in a token and fetches files from your Dropbox account.
47 | - Download - [ [TS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/typescript/download), [JS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/javascript/download) ] - An example showing how to download a shared file.
48 | - Team As User - [ [TS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/typescript/team-as-user), [JS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/javascript/team-as-user) ] - An example showing how to act as a user.
49 | - Team - [ [TS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/typescript/team), [JS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/javascript/team) ] - An example showing how to use the team functionality and list team devices.
50 | - Upload [ [TS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/typescript/upload), [JS](https://github.com/dropbox/dropbox-sdk-js/tree/main/examples/javascript/upload) ] - An example showing how to upload a file to Dropbox.
51 |
52 | ## Getting Help
53 |
54 | If you find a bug, please see [CONTRIBUTING.md][contributing] for information on how to report it.
55 |
56 | If you need help that is not specific to this SDK, please reach out to [Dropbox Support][support].
57 |
58 | ## License
59 |
60 | This SDK is distributed under the MIT license, please see [LICENSE][license] for more information.
61 |
62 | [logo]: https://cfl.dropboxstatic.com/static/images/sdk/javascript_banner.png
63 | [repo]: https://github.com/dropbox/dropbox-sdk-js
64 | [documentation]: https://dropbox.github.io/dropbox-sdk-js/
65 | [examples]: https://github.com/dropbox/dropbox-sdk-js/tree/main/examples
66 | [license]: https://github.com/dropbox/dropbox-sdk-js/blob/main/LICENSE
67 | [contributing]: https://github.com/dropbox/dropbox-sdk-js/blob/main/CONTRIBUTING.md
68 | [devconsole]: https://dropbox.com/developers/apps
69 | [oauthguide]: https://www.dropbox.com/lp/developers/reference/oauth-guide
70 | [support]: https://www.dropbox.com/developers/contact
71 |
--------------------------------------------------------------------------------
/UPGRADING.md:
--------------------------------------------------------------------------------
1 | # Upgrading the Dropbox SDK
2 |
3 | This document is designed to show you how to upgrade to the latest version of the SDK accomodating any breaking changes introduced by major version updates. If you find any issues with either this guide on upgrading or the changes introduced in the new version, please see [CONTRIBUTING.md][contributing]
4 |
5 | # Upgrading from v9.X.X to v10.0.0
6 |
7 | ## 1. Deprecating the `authenticateWithCordova` function
8 |
9 | The `authenticateWithCordova` function used an in-app browser within the Cordova framework to authenticate users via OAuth. As a part of hardening security, we are following [Google’s recommendation](https://developers.googleblog.com/2016/08/modernizing-oauth-interactions-in-native-apps.html) to remove support for authentication via a “web-view” or in-app browsers. Since the `authenticateWithCordova` function relies on running in an in-app browser, we have made the choice to deprecate this function.
10 |
11 | Instead, apps will need to implement logic to handle this use case. The high level logic would be as follows:
12 |
13 | 1. getAuthenticationUrl with your app’s parameters. For Native Apps, we highly encourage using PKCE to increase your app’s security.
14 | 2. Open the authentication URL in the default system browser
15 | 3. Redirect back into your app upon completion of the OAuth flow.
16 |
17 | We recommend using a custom URI for redirect to ensure you are redirecting directly back into your app. You can read up on this process more in detail on the [OAuth site](https://www.oauth.com/oauth2-servers/redirect-uris/redirect-uris-native-apps/).
18 |
19 | # Upgrading from v8.X.X to v9.0.0
20 |
21 | ## 1. Unblocking browser PKCE flow
22 |
23 | Previously, there was an issue in which Node and the Browser use different processes to generate the `codeVerifier` and `codeChallenge`. In order to remedy this, both `generatePKCECodes` and `getAuthenticationUrl` now return promises due to the how the browser digests hashes.
24 |
25 | Previous Implementation(synchronous):
26 | ```
27 | var authUrl = dbxAuth.getAuthenticationUrl(redirectUri, null, 'code', 'offline', null, 'none', false)
28 | // logic for navigating to authUrl
29 | ```
30 | New Implementation(async):
31 | ```
32 | dbxAuth.getAuthenticationUrl(redirectUri, null, 'code', 'offline', null, 'none', false)
33 | .then((authUrl) => {
34 | // logic for navigating to authUrl
35 | });
36 | ```
37 | # Upgrading from v7.X.X to v8.0.0
38 |
39 | ## 1. Throwing Errors as `DropboxResponseError` rather than a literal object
40 |
41 | We have created a new Error class called `DropboxResponseError` which contains the same members as the literal that was thrown, but in a cleaner format. It also allows you to leverage the fact this class now extends the builtin `Error` class.
42 |
43 | # Upgrading from v6.X.X to v7.0.0
44 |
45 | ## 1. Fixing the Typescript argument parameter bug ([#41](https://github.com/dropbox/dropbox-sdk-js/issues/41))
46 |
47 | We noticed a long lasting bug where the Typescript definitions of routes with no arg would require a `void` argument. This required users to make calls like this:
48 |
49 | ```
50 | var result = dbx.usersGetCurrentAccount(null);
51 | ```
52 |
53 | We have since fixed this to no longer require the null parameter.
54 |
55 | # Upgrading from v5.X.X to v6.0.0
56 |
57 | ## 1. Unifying Dropbox and DropboxTeam
58 |
59 | We made the decision to unify the Dropbox and DropboxTeam objects to further simplify the logic in the SDK. Migrating is very straightforward, a reference like this:
60 |
61 | ```
62 | var dbx = new DropboxTeam({
63 | accessToken: 'my_token'
64 | });
65 | ```
66 |
67 | Can be rewritten as:
68 |
69 | ```
70 | var dbx = new Dropbox({
71 | accessToken: 'my_token'
72 | });
73 | ```
74 |
75 | Additionally, when using features like assume user, select admin, or path root they are not set as a part of the constructor rather than creating a new client. Logic like this:
76 |
77 | ```
78 | var dbx = new DropboxTeam({
79 | accessToken: 'my_token'
80 | });
81 | var dbx_user = dbx.actAsUser(user_id);
82 | dbx_user.usersGetCurrentAccount();
83 | ```
84 |
85 | Can be rewritten as:
86 |
87 | ```
88 | var dbx = new Dropbox({
89 | accessToken: 'my_token',
90 | selectUser: 'my_user_id'
91 | });
92 | dbx.usersGetcurrentAccount();
93 | ```
94 |
95 | ## 2. Moving authentication to DropboxAuth
96 |
97 | Another change that was made was to move all auth related functionality into the DropboxAuth object. The main Dropbox object can be constructed the same way but this will internally create a DropboxAuth object. In order to access any auth functions from the main client you must change your code as such:
98 |
99 | ```
100 | dbx.get_authentication_url(...);
101 | ```
102 |
103 | Would become something like this:
104 |
105 | ```
106 | dbx.auth.get_authentication_url(...);
107 | ```
108 |
109 | However, we recommend creating a DropboxAuth object before creating a client and then constructing as such:
110 |
111 | ```
112 | var dbxAuth = new DropboxAuth();
113 | ... // Do auth logic
114 | var dbx = new Dropbox(dbxAuth);
115 | ```
116 |
117 | That way if you need to create another instance of the client, you can easily plug in the same auth object.
118 |
119 | ## 3. Changing Typescript export format
120 |
121 | We have updated the Typescript definitions to be a part of `Dropbox` namespace rather than the `DropboxTypes` namespace. This would look like:
122 |
123 | ```
124 | const result: DropboxTypes.users.FullAccount dbx.usersGetCurrentAccount();
125 | ```
126 |
127 | Would become:
128 |
129 | ```
130 | const result: Dropbox.users.FullAccount dbx.usersGetCurrentAccount();
131 | ```
132 |
133 | ## 4. Updating the Response object
134 |
135 | We have wrapped the raw responses into the `DropboxResponse` object in order to expose more information out to users. This change looks like:
136 |
137 | ```
138 | var response = dbx.usersGetcurrentAccount();
139 | console.log(response.fileBlob); //or fileBinary if using workers
140 | ```
141 |
142 | Would become:
143 |
144 | ```
145 | var response = dbx.usersGetcurrentAccount();
146 | console.log(response.result.fileBlob); //or fileBinary if using workers
147 | ```
148 |
149 | This also exposes the other components of the response like the status and headers which was not previously available.
150 |
151 | ```
152 | var response = dbx.usersGetcurrentAccount();
153 | console.log(response.status);
154 | console.log(response.headers);
155 | ```
156 |
157 | ## 5. Default behavior for `fetch`.
158 |
159 | Previously we have provided guidance to SDK users that they should not rely on the Dropbox SDK's global fetch and that it would be deprecated in future versions. In 6.0.0 onwards, we now include the `node-fetch` dependency as part of the NPM package. For browser environments, we fallback to `window.fetch` by default.
160 |
161 | As a result, you should not pass in your own `fetch` to the Dropbox constructor unless you have a specific reason to do so (mocking, etc). Note that if you opt to pass in fetch to support your use case, you may need to bind your fetch to the appropriate context e.g. `fetch.bind(your_context)`.
162 |
163 | [contributing]: https://github.com/dropbox/dropbox-sdk-js/blob/main/CONTRIBUTING.md
164 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | status:
3 | project:
4 | default:
5 | target: auto
6 | threshold: 0%
7 | base: auto
8 | if_not_found: error
9 | if_ci_failed: error
10 | informational: false
11 | only_pulls: true
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Dropbox JavaScript SDK Examples
2 |
3 | To run the examples in your development environment:
4 |
5 | 1. Clone this repo
6 | 2. Run `npm install` and `npm run build`
7 | 3. Start the development server with `node server.js`.
8 | 4. Point your browser to
9 |
10 | # Dropbox TypeScript SDK Examples
11 |
12 | To run the examples in your development environment:
13 |
14 | 1. Clone this repo
15 | 2. Run `npm install` and `npm run build`
16 | 3. Run the example you want (basic, download, team, team-as-user, or upload)
17 | e.g. `node basic`
18 |
19 | ## Code flow example
20 |
21 | 1. Clone this repo
22 | 2. Run `npm install` and `npm run build` in the root of the repository
23 | 3. Create an app in the [App console](https://www.dropbox.com/developers/apps).
24 | 4. Set a redirect URI "http://localhost:3000/auth" on the app's page on the [App console](https://www.dropbox.com/developers/apps).
25 | 5. Set app key and secret in `examples/javascript/simple-backend/code_flow_example.js` on lines 17 and 18.
26 | 6. Run `node examples/javascript/simple-backend/code_flow_example.js`
27 | 7. Point your browser to
28 |
--------------------------------------------------------------------------------
/examples/javascript/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "no-console": 0,
4 | "import/no-extraneous-dependencies": 0
5 | }
6 | }
--------------------------------------------------------------------------------
/examples/javascript/PKCE-backend/code_flow_example.js:
--------------------------------------------------------------------------------
1 | // Standalone example to demonstrate codeflow.
2 | // Start the server, hit localhost:3000 on the browser, and click through.
3 | // On the server logs, you should have the auth code, as well as the token
4 | // from exchanging it. This exchange is invisible to the app user
5 |
6 | const fetch = require('node-fetch');
7 | const app = require('express')();
8 |
9 | const hostname = 'localhost';
10 | const port = 3000;
11 |
12 | const config = {
13 | fetch,
14 | clientId: '42zjexze6mfpf7x',
15 | };
16 |
17 | const { Dropbox } = require('dropbox'); // eslint-disable-line import/no-unresolved
18 |
19 | const dbx = new Dropbox(config);
20 |
21 | const redirectUri = `http://${hostname}:${port}/auth`;
22 |
23 | app.get('/', (req, res) => {
24 | dbx.auth.getAuthenticationUrl(redirectUri, null, 'code', 'offline', null, 'none', true)
25 | .then((authUrl) => {
26 | res.writeHead(302, { Location: authUrl });
27 | res.end();
28 | });
29 | });
30 |
31 | app.get('/auth', (req, res) => { // eslint-disable-line no-unused-vars
32 | const { code } = req.query;
33 | console.log(`code:${code}`);
34 |
35 | dbx.auth.getAccessTokenFromCode(redirectUri, code)
36 | .then((token) => {
37 | console.log(`Token Result:${JSON.stringify(token)}`);
38 | dbx.auth.setRefreshToken(token.result.refresh_token);
39 | dbx.usersGetCurrentAccount()
40 | .then((response) => {
41 | console.log('response', response);
42 | })
43 | .catch((error) => {
44 | console.error(error);
45 | });
46 | })
47 | .catch((error) => {
48 | console.error(error);
49 | });
50 | res.end();
51 | });
52 |
53 | app.listen(port);
54 |
--------------------------------------------------------------------------------
/examples/javascript/PKCE-backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "PKCE-backend",
3 | "version": "1.0.0",
4 | "dependencies": {
5 | "dropbox": "file:../../../"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/examples/javascript/auth/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This example takes the user through Dropbox's API OAuth flow using Dropbox.getAuthenticationUrl() method [docs] and then uses the generated access token to list the contents of their root directory.
This example fetches the contents of your root Dropbox directory. It uses the Dropbox.filesListFolder() method [docs].
32 |
33 |
37 |
38 |
39 |
40 |
41 |
To obtain an access token for quick testing, you can go to API Explorer click the "Get Token" button on the top right, copy the token it creates and then paste it here.
This example shows how to use the Dropbox.sharingGetSharedLinkFile() [docs] method to download the file for the given shared link.
32 |
33 |
38 |
39 |
40 |
41 |
42 |
To obtain an access token for quick testing, you can go to API Explorer click the "Get Token" button on the top right, copy the token it creates and then paste it here.
This example shows how to use the Dropbox class and the selectUser [docs] parameter, to retreive a Dropbox class that is acting as a specific user on the team.
This example shows how to use the Dropbox.filesUpload() [docs] method to upload a file.
30 |
31 |
36 |
37 |
38 |
39 |
40 |
To obtain an access token for quick testing, you can go to API Explorer click the "Get Token" button on the top right, copy the token it creates and then paste it here.
41 |
42 |
43 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/examples/javascript/utils.js:
--------------------------------------------------------------------------------
1 | (function (window) {
2 | window.utils = {
3 | parseQueryString(str) {
4 | const ret = Object.create(null);
5 |
6 | if (typeof str !== 'string') {
7 | return ret;
8 | }
9 |
10 | str = str.trim().replace(/^(\?|#|&)/, '');
11 |
12 | if (!str) {
13 | return ret;
14 | }
15 |
16 | str.split('&').forEach((param) => {
17 | const parts = param.replace(/\+/g, ' ').split('=');
18 | // Firefox (pre 40) decodes `%3D` to `=`
19 | // https://github.com/sindresorhus/query-string/pull/37
20 | let key = parts.shift();
21 | let val = parts.length > 0 ? parts.join('=') : undefined;
22 |
23 | key = decodeURIComponent(key);
24 |
25 | // missing `=` should be `null`:
26 | // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
27 | val = val === undefined ? null : decodeURIComponent(val);
28 |
29 | if (ret[key] === undefined) {
30 | ret[key] = val;
31 | } else if (Array.isArray(ret[key])) {
32 | ret[key].push(val);
33 | } else {
34 | ret[key] = [ret[key], val];
35 | }
36 | });
37 |
38 | return ret;
39 | },
40 | };
41 | }(window));
42 |
--------------------------------------------------------------------------------
/examples/typescript/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["airbnb-base"],
3 | "env": {
4 | "browser": false,
5 | "node": true
6 | },
7 | "rules": {
8 | "func-names": 0,
9 | "no-param-reassign": 0,
10 | "import/prefer-default-export": 0,
11 | "no-console": 0,
12 | "import/no-extraneous-dependencies": 0
13 | },
14 | "plugins": ["@typescript-eslint"],
15 | "parser": "@typescript-eslint/parser",
16 | "parserOptions": { "ecmaVersion": 6 }
17 | }
--------------------------------------------------------------------------------
/examples/typescript/basic/basic.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["basic.ts"],"names":[],"mappings":";AAAA,qCAAkC;AAElC,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjC,MAAM,CAAC,KAAK,EAAE,CAAC;AAEf,MAAM,CAAC,GAAG,CAAC;IACT,UAAU,EAAE;QACV,WAAW,EAAE;YACX,WAAW,EAAE,qCAAqC;SACnD;KACF;CACF,EAAE,CAAC,KAAU,EAAE,MAAW,EAAE,EAAE;IAC7B,MAAM,GAAG,GAAG,IAAI,iBAAO,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7D,GAAG,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;SAC9B,IAAI,CAAC,CAAC,QAAa,EAAE,EAAE;QACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAAQ,EAAE,EAAE;QAClB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
--------------------------------------------------------------------------------
/examples/typescript/basic/basic.ts:
--------------------------------------------------------------------------------
1 | import { Dropbox } from 'dropbox';
2 |
3 | const prompt = require('prompt');
4 |
5 | prompt.start();
6 |
7 | prompt.get({
8 | properties: {
9 | accessToken: {
10 | description: 'Please enter an API V2 access token',
11 | },
12 | },
13 | }, (error: any, result: any) => {
14 | const dbx = new Dropbox({ accessToken: result.accessToken });
15 | dbx.filesListFolder({ path: '' })
16 | .then((response: any) => {
17 | console.log(response);
18 | })
19 | .catch((err: any) => {
20 | console.log(err);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/examples/typescript/basic/index.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports, "__esModule", { value: true });
2 | const dropbox_1 = require("dropbox");
3 | const prompt = require('prompt');
4 | prompt.start();
5 | prompt.get({
6 | properties: {
7 | accessToken: {
8 | description: 'Please enter an API V2 access token',
9 | },
10 | },
11 | }, (error, result) => {
12 | const dbx = new dropbox_1.Dropbox({ accessToken: result.accessToken });
13 | dbx.filesListFolder({ path: '' })
14 | .then((response) => {
15 | console.log(response);
16 | })
17 | .catch((err) => {
18 | console.log(err);
19 | });
20 | });
21 | //# sourceMappingURL=basic.js.map
--------------------------------------------------------------------------------
/examples/typescript/download/download.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["download.ts"],"names":[],"mappings":";AAAA,qCAAkD,CAAC,qCAAqC;AACxF,yBAA0B;AAE1B,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjC,MAAM,CAAC,KAAK,EAAE,CAAC;AAEf,MAAM,CAAC,GAAG,CAAC;IACT,UAAU,EAAE;QACV,WAAW,EAAE;YACX,WAAW,EAAE,qCAAqC;SACnD;QACD,UAAU,EAAE;YACV,WAAW,EAAE,sCAAsC;SACpD;KACF;CACF,EAAE,CAAC,KAAU,EAAE,MAAW,EAAE,EAAE;IAC7B,MAAM,GAAG,GAAG,IAAI,iBAAO,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7D,GAAG,CAAC,wBAAwB,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;SACrD,IAAI,CAAC,CAAC,IAAS,EAAE,EAAE;QAClB,4DAA4D;QAC5D,+DAA+D;QAC/D,6BAA6B;QAC7B,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAS,IAAK,CAAC,UAAU,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE;YAC/E,IAAI,GAAG,EAAE;gBAAE,MAAM,GAAG,CAAC;aAAE;YACvB,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,IAAI,SAAS,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAA0C,EAAE,EAAE;QACpD,MAAM,GAAG,CAAC;IACZ,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
--------------------------------------------------------------------------------
/examples/typescript/download/download.ts:
--------------------------------------------------------------------------------
1 | import { Dropbox, Error, sharing } from 'dropbox'; // eslint-disable-line no-unused-vars
2 | import fs = require('fs');
3 |
4 | const prompt = require('prompt');
5 |
6 | prompt.start();
7 |
8 | prompt.get({
9 | properties: {
10 | accessToken: {
11 | description: 'Please enter an API V2 access token',
12 | },
13 | sharedLink: {
14 | description: 'Please enter a shared link to a file',
15 | },
16 | },
17 | }, (error: any, result: any) => {
18 | const dbx = new Dropbox({ accessToken: result.accessToken });
19 | dbx.sharingGetSharedLinkFile({ url: result.sharedLink })
20 | .then((data: any) => {
21 | // Note: The fileBinary field is not part of the Dropbox SDK
22 | // specification, so it is not included in the TypeScript type.
23 | // It is injected by the SDK.
24 | fs.writeFile(data.result.name, ( data).result.fileBinary, { encoding: 'binary' }, (err) => {
25 | if (err) { throw err; }
26 | console.log(`File: ${data.result.name} saved.`);
27 | });
28 | })
29 | .catch((err: Error) => {
30 | throw err;
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/examples/typescript/download/index.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports, "__esModule", { value: true });
2 | const dropbox_1 = require("dropbox"); // eslint-disable-line no-unused-vars
3 | const fs = require("fs");
4 | const prompt = require('prompt');
5 | prompt.start();
6 | prompt.get({
7 | properties: {
8 | accessToken: {
9 | description: 'Please enter an API V2 access token',
10 | },
11 | sharedLink: {
12 | description: 'Please enter a shared link to a file',
13 | },
14 | },
15 | }, (error, result) => {
16 | const dbx = new dropbox_1.Dropbox({ accessToken: result.accessToken });
17 | dbx.sharingGetSharedLinkFile({ url: result.sharedLink })
18 | .then((data) => {
19 | // Note: The fileBinary field is not part of the Dropbox SDK
20 | // specification, so it is not included in the TypeScript type.
21 | // It is injected by the SDK.
22 | fs.writeFile(data.result.name, data.result.fileBinary, { encoding: 'binary' }, (err) => {
23 | if (err) {
24 | throw err;
25 | }
26 | console.log(`File: ${data.result.name} saved.`);
27 | });
28 | })
29 | .catch((err) => {
30 | throw err;
31 | });
32 | });
33 | //# sourceMappingURL=download.js.map
--------------------------------------------------------------------------------
/examples/typescript/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dropbox-ts-examples",
3 | "version": "1.0.0",
4 | "description": "Dropbox TypeScript SDK Examples",
5 | "scripts": {
6 | "build": "tsc -p tsconfig.json"
7 | },
8 | "contributors": [
9 | "Brad Rogers ",
10 | "Andrew Lawson ",
11 | "John Vilk "
12 | ],
13 | "license": "MIT",
14 | "engines": {
15 | "node": ">=0.10"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/dropbox/dropbox-sdk-js.git"
20 | },
21 | "dependencies": {
22 | "dropbox": "file:../../"
23 | },
24 | "devDependencies": {
25 | "@types/express": "^4.17.11",
26 | "@types/node": "^14.14.25",
27 | "typescript": "^4.1.4"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/typescript/team-as-user/index.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports, "__esModule", { value: true });
2 | const dropbox_1 = require("dropbox"); // eslint-disable-line no-unused-vars
3 | const prompt = require('prompt');
4 | prompt.start();
5 | prompt.get({
6 | properties: {
7 | accessToken: {
8 | description: 'Please enter an API V2 team access token',
9 | },
10 | userId: {
11 | description: 'Please enter the id of the user you would like to act as',
12 | },
13 | },
14 | }, (error, result) => {
15 | const dbx = new dropbox_1.Dropbox({ accessToken: result.accessToken, selectUser: result.userId });
16 | dbx.filesListFolder({ path: '' })
17 | .then((response) => {
18 | console.log(response);
19 | })
20 | .catch((err) => {
21 | console.log(err);
22 | });
23 | });
24 | //# sourceMappingURL=team-as-user.js.map
--------------------------------------------------------------------------------
/examples/typescript/team-as-user/team-as-user.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["team-as-user.ts"],"names":[],"mappings":";AAAA,qCAAgD,CAAC,qCAAqC;AAEtF,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjC,MAAM,CAAC,KAAK,EAAE,CAAC;AAEf,MAAM,CAAC,GAAG,CAAC;IACT,UAAU,EAAE;QACV,WAAW,EAAE;YACX,WAAW,EAAE,0CAA0C;SACxD;QACD,MAAM,EAAE;YACN,WAAW,EAAE,0DAA0D;SACxE;KACF;CACF,EAAE,CAAC,KAAU,EAAE,MAAW,EAAE,EAAE;IAC7B,MAAM,GAAG,GAAG,IAAI,iBAAO,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACxF,GAAG,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;SAC9B,IAAI,CAAC,CAAC,QAAa,EAAE,EAAE;QACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAAiC,EAAE,EAAE;QAC3C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
--------------------------------------------------------------------------------
/examples/typescript/team-as-user/team-as-user.ts:
--------------------------------------------------------------------------------
1 | import { Dropbox, Error, files } from 'dropbox'; // eslint-disable-line no-unused-vars
2 |
3 | const prompt = require('prompt');
4 |
5 | prompt.start();
6 |
7 | prompt.get({
8 | properties: {
9 | accessToken: {
10 | description: 'Please enter an API V2 team access token',
11 | },
12 | userId: {
13 | description: 'Please enter the id of the user you would like to act as',
14 | },
15 | },
16 | }, (error: any, result: any) => {
17 | const dbx = new Dropbox({ accessToken: result.accessToken, selectUser: result.userId });
18 | dbx.filesListFolder({ path: '' })
19 | .then((response: any) => {
20 | console.log(response);
21 | })
22 | .catch((err: Error) => {
23 | console.log(err);
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/examples/typescript/team/index.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports, "__esModule", { value: true });
2 | const dropbox_1 = require("dropbox"); // eslint-disable-line no-unused-vars
3 | const prompt = require('prompt');
4 | prompt.start();
5 | prompt.get({
6 | properties: {
7 | accessToken: {
8 | description: 'Please enter an API V2 team access token',
9 | },
10 | },
11 | }, (error, result) => {
12 | const dbx = new dropbox_1.Dropbox({ accessToken: result.accessToken });
13 | dbx.teamDevicesListTeamDevices({})
14 | .then((response) => {
15 | console.log('Devices', response);
16 | })
17 | .catch((err) => {
18 | console.log(err);
19 | });
20 | });
21 | //# sourceMappingURL=team.js.map
--------------------------------------------------------------------------------
/examples/typescript/team/team.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["team.ts"],"names":[],"mappings":";AAAA,qCAA+C,CAAC,qCAAqC;AAErF,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjC,MAAM,CAAC,KAAK,EAAE,CAAC;AAEf,MAAM,CAAC,GAAG,CAAC;IACT,UAAU,EAAE;QACV,WAAW,EAAE;YACX,WAAW,EAAE,0CAA0C;SACxD;KACF;CACF,EAAE,CAAC,KAAU,EAAE,MAAW,EAAE,EAAE;IAC7B,MAAM,GAAG,GAAG,IAAI,iBAAO,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7D,GAAG,CAAC,0BAA0B,CAAC,EAAE,CAAC;SAC/B,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;QACjB,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,GAAqC,EAAE,EAAE;QAC/C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
--------------------------------------------------------------------------------
/examples/typescript/team/team.ts:
--------------------------------------------------------------------------------
1 | import { Dropbox, Error, team } from 'dropbox'; // eslint-disable-line no-unused-vars
2 |
3 | const prompt = require('prompt');
4 |
5 | prompt.start();
6 |
7 | prompt.get({
8 | properties: {
9 | accessToken: {
10 | description: 'Please enter an API V2 team access token',
11 | },
12 | },
13 | }, (error: any, result: any) => {
14 | const dbx = new Dropbox({ accessToken: result.accessToken });
15 | dbx.teamDevicesListTeamDevices({})
16 | .then((response) => {
17 | console.log('Devices', response);
18 | })
19 | .catch((err: Error) => {
20 | console.log(err);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/examples/typescript/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es2015",
5 | "lib": ["dom", "es2015", "es2016", "es2017"],
6 | "moduleResolution": "node",
7 | "sourceMap": true,
8 | "declaration": false,
9 | "noFallthroughCasesInSwitch": true,
10 | "noImplicitAny": true,
11 | "noImplicitReturns": true,
12 | "noImplicitThis": true,
13 | "noImplicitUseStrict": true,
14 | "noUnusedLocals": true,
15 | "outDir": "dist"
16 | },
17 | "include": ["**/*.ts", "node/**/*.ts"],
18 | "exclude": ["node_modules"]
19 | }
20 |
--------------------------------------------------------------------------------
/examples/typescript/upload/index.js:
--------------------------------------------------------------------------------
1 | Object.defineProperty(exports, "__esModule", { value: true });
2 | const dropbox_1 = require("dropbox"); // eslint-disable-line no-unused-vars
3 | const fs = require("fs");
4 | const path = require("path");
5 | const prompt = require('prompt');
6 | prompt.start();
7 | prompt.get({
8 | properties: {
9 | accessToken: {
10 | description: 'Please enter an API V2 access token',
11 | },
12 | },
13 | }, (error, result) => {
14 | const dbx = new dropbox_1.Dropbox({ accessToken: result.accessToken });
15 | fs.readFile(path.join(__dirname, '/basic.js'), 'utf8', (err, contents) => {
16 | if (err) {
17 | console.log('Error: ', err);
18 | }
19 | // This uploads basic.js to the root of your dropbox
20 | dbx.filesUpload({ path: '/basic.js', contents })
21 | .then((response) => {
22 | console.log(response);
23 | })
24 | .catch((uploadErr) => {
25 | console.log(uploadErr);
26 | });
27 | });
28 | });
29 | //# sourceMappingURL=upload.js.map
--------------------------------------------------------------------------------
/examples/typescript/upload/upload.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["upload.ts"],"names":[],"mappings":";AAAA,qCAAgD,CAAC,qCAAqC;AACtF,yBAA0B;AAC1B,6BAA8B;AAE9B,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEjC,MAAM,CAAC,KAAK,EAAE,CAAC;AAEf,MAAM,CAAC,GAAG,CAAC;IACT,UAAU,EAAE;QACV,WAAW,EAAE;YACX,WAAW,EAAE,qCAAqC;SACnD;KACF;CACF,EAAE,CAAC,KAAU,EAAE,MAAW,EAAE,EAAE;IAC7B,MAAM,GAAG,GAAG,IAAI,iBAAO,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAE7D,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE;QACvE,IAAI,GAAG,EAAE;YACP,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;SAC7B;QAED,oDAAoD;QACpD,GAAG,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;aAC7C,IAAI,CAAC,CAAC,QAAa,EAAE,EAAE;YACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxB,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,SAAmC,EAAE,EAAE;YAC7C,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
--------------------------------------------------------------------------------
/examples/typescript/upload/upload.ts:
--------------------------------------------------------------------------------
1 | import { Dropbox, Error, files } from 'dropbox'; // eslint-disable-line no-unused-vars
2 | import fs = require('fs');
3 | import path = require('path');
4 |
5 | const prompt = require('prompt');
6 |
7 | prompt.start();
8 |
9 | prompt.get({
10 | properties: {
11 | accessToken: {
12 | description: 'Please enter an API V2 access token',
13 | },
14 | },
15 | }, (error: any, result: any) => {
16 | const dbx = new Dropbox({ accessToken: result.accessToken });
17 |
18 | fs.readFile(path.join(__dirname, '/basic.js'), 'utf8', (err, contents) => {
19 | if (err) {
20 | console.log('Error: ', err);
21 | }
22 |
23 | // This uploads basic.js to the root of your dropbox
24 | dbx.filesUpload({ path: '/basic.js', contents })
25 | .then((response: any) => {
26 | console.log(response);
27 | })
28 | .catch((uploadErr: Error) => {
29 | console.log(uploadErr);
30 | });
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/generator/generate_routes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from __future__ import absolute_import, division, print_function, unicode_literals
3 |
4 | import argparse
5 | import glob
6 | import json
7 | import os
8 | import subprocess
9 | import sys
10 |
11 | cmdline_desc = """\
12 | Runs Stone to generate JS routes for the Dropbox client.
13 | """
14 |
15 | _cmdline_parser = argparse.ArgumentParser(description=cmdline_desc)
16 | _cmdline_parser.add_argument(
17 | '-v',
18 | '--verbose',
19 | action='store_true',
20 | help='Print debugging statements.',
21 | )
22 | _cmdline_parser.add_argument(
23 | 'spec',
24 | nargs='*',
25 | type=str,
26 | help='Path to API specifications. Each must have a .stone extension.',
27 | )
28 | _cmdline_parser.add_argument(
29 | '-s',
30 | '--stone',
31 | type=str,
32 | help='Path to clone of stone repository.',
33 | )
34 |
35 |
36 | def main():
37 | """The entry point for the program."""
38 |
39 | args = _cmdline_parser.parse_args()
40 | verbose = args.verbose
41 |
42 | if args.spec:
43 | specs = args.spec
44 | else:
45 | # If no specs were specified, default to the spec submodule.
46 | specs = glob.glob('dropbox-api-spec/*.stone') # Arbitrary sorting
47 | specs.sort()
48 |
49 | specs = [os.path.join(os.getcwd(), s) for s in specs]
50 |
51 | stone_path = os.path.abspath('stone')
52 | if args.stone:
53 | stone_path = args.stone
54 |
55 | dropbox_pkg_path = os.path.abspath(
56 | os.path.join(os.path.dirname(sys.argv[0]), '../lib'))
57 | if verbose:
58 | print('Dropbox package path: %s' % dropbox_pkg_path)
59 |
60 | typescript_template_path = os.path.abspath(
61 | os.path.join(os.path.dirname(sys.argv[0]), 'typescript'))
62 | if verbose:
63 | print('TypeScript template path: %s' % typescript_template_path)
64 |
65 | types_template_path = os.path.abspath(
66 | os.path.join(os.path.dirname(sys.argv[0]), '../types'))
67 | if verbose:
68 | print('Types template path: %s' % types_template_path)
69 |
70 | upload_arg = {
71 | "match": ["style", "upload"],
72 | "arg_name": "contents",
73 | "arg_type": "Object",
74 | "arg_docstring": "The file contents to be uploaded."
75 | }
76 |
77 | if verbose:
78 | print('Generating JS types')
79 | subprocess.check_output(
80 | (['python3', '-m', 'stone.cli', 'js_types', dropbox_pkg_path] +
81 | specs + ['-b', 'team'] + ['-a', 'host', '-a', 'style', '-a', 'auth'] +
82 | ['--', 'types.js', '-e', json.dumps(upload_arg)]),
83 | cwd=stone_path)
84 |
85 | if verbose:
86 | print('Generating JS client routes for user routes')
87 | o = subprocess.check_output(
88 | (['python3', '-m', 'stone.cli', 'js_client', dropbox_pkg_path] +
89 | specs + ['-a', 'host', '-a', 'style', '-a', 'auth', '-a', 'scope'] +
90 | ['--', 'routes.js', '-c', 'Dropbox', '--wrap-response-in', 'DropboxResponse', '--wrap-error-in', 'DropboxResponseError', '-a', 'scope']),
91 | cwd=stone_path)
92 | if verbose:
93 | print(o)
94 |
95 | if verbose:
96 | print('Generating TSD types')
97 | subprocess.check_output(
98 | (['python3', '-m', 'stone.cli', 'tsd_types', typescript_template_path] +
99 | specs + ['-b', 'team'] + ['-a', 'host', '-a', 'style'] +
100 | ['--', 'dropbox_types.d.tstemplate', 'dropbox_types.d.ts', '-e', json.dumps(upload_arg), '--export-namespaces']),
101 | cwd=stone_path)
102 |
103 | if verbose:
104 | print('Generating TSD client routes for user routes')
105 | subprocess.check_output(
106 | (['python3', '-m', 'stone.cli', 'tsd_client', typescript_template_path] +
107 | specs + ['-a', 'host', '-a', 'style', '-a', 'scope'] +
108 | ['--', 'index.d.tstemplate', 'index.d.ts', '--wrap-response-in', 'DropboxResponse', '--wrap-error-in', 'DropboxResponseError', '--import-namespaces', '--types-file', './dropbox_types', '-a', 'scope']),
109 | cwd=stone_path)
110 |
111 | typescript_generated_files = glob.glob('typescript/*.d.ts')
112 | typescript_generated_files.sort()
113 | typescript_generated_files = [os.path.join(os.getcwd(), f) for f in typescript_generated_files]
114 | if verbose:
115 | print('TypeScript generated files: %s' % typescript_generated_files)
116 |
117 | if verbose:
118 | print('Moving TSD routes and types to types/')
119 | for file in typescript_generated_files:
120 | subprocess.check_output(
121 | (['mv', file , types_template_path]),
122 | cwd=typescript_template_path
123 | )
124 |
125 | if __name__ == '__main__':
126 | main()
127 |
--------------------------------------------------------------------------------
/generator/typescript/dropbox_types.d.tstemplate:
--------------------------------------------------------------------------------
1 | // Auto-generated by Stone, do not modify.
2 |
3 | /*TYPES*/
4 |
--------------------------------------------------------------------------------
/generator/typescript/index.d.tstemplate:
--------------------------------------------------------------------------------
1 | // Auto-generated by Stone, do not modify.
2 |
3 | /*IMPORT*/
4 | export * from './dropbox_types';
5 |
6 | export interface DropboxAuthOptions {
7 | // An access token for making authenticated requests.
8 | accessToken?: string;
9 | // The time at which the access token expires.
10 | accessTokenExpiresAt?: Date;
11 | // A refresh token for retrieving access tokens
12 | refreshToken?: string;
13 | // The client id for your app. Used to create authentication URL.
14 | clientId?: string;
15 | // The client secret for your app. Used for refresh and token exchange.
16 | clientSecret?: string;
17 | // The fetch library for making requests.
18 | fetch?: Function;
19 | // A custom domain to use when making api requests. This should only be used for testing as scaffolding to avoid making network requests.
20 | domain?: string;
21 | // A custom delimiter to use when separating domain from subdomain. This should only be used for testing as scaffolding.
22 | domainDelimiter?: string;
23 | // An object (in the form of header: value) designed to set custom headers to use during a request.
24 | customHeaders?: object;
25 | // Whether request data is sent on body or as URL params. Defaults to false.
26 | dataOnBody?: boolean;
27 | }
28 |
29 | export class DropboxAuth {
30 | /**
31 | * The DropboxAuth class that provides methods to manage, acquire, and refresh tokens.
32 | */
33 | constructor();
34 |
35 | /**
36 | * The DropboxAuth class that provides methods to manage, acquire, and refresh tokens.
37 | */
38 | constructor(options: DropboxAuthOptions);
39 |
40 | /**
41 | * Get the access token
42 | * @returns {String} Access token
43 | */
44 | getAccessToken(): string;
45 |
46 | /**
47 | * Get an OAuth2 access token from an OAuth2 Code.
48 | * @param redirectUri A URL to redirect the user to after authenticating.
49 | * This must be added to your app through the admin interface.
50 | * @param code An OAuth2 code.
51 | * @returns {Object} An object containing the token and related info (if applicable)
52 | */
53 | getAccessTokenFromCode(redirectUri: string, code: string): Promise>;
54 |
55 | /**
56 | * Get a URL that can be used to authenticate users for the Dropbox API.
57 | * @arg {String} redirectUri - A URL to redirect the user to after
58 | * authenticating. This must be added to your app through the admin interface.
59 | * @arg {String} [state] - State that will be returned in the redirect URL to help
60 | * prevent cross site scripting attacks.
61 | * @arg {String} [authType] - auth type, defaults to 'token', other option is 'code'
62 | * @arg {String} [tokenAccessType] - type of token to request. From the following:
63 | * null - creates a token with the app default (either legacy or online)
64 | * legacy - creates one long-lived token with no expiration
65 | * online - create one short-lived token with an expiration
66 | * offline - create one short-lived token with an expiration with a refresh token
67 | * @arg {Array} [scope] - scopes to request for the grant
68 | * @arg {String} [includeGrantedScopes] - whether or not to include previously granted scopes.
69 | * From the following:
70 | * user - include user scopes in the grant
71 | * team - include team scopes in the grant
72 | * Note: if this user has never linked the app, include_granted_scopes must be None
73 | * @arg {boolean} [usePKCE] - Whether or not to use Sha256 based PKCE. PKCE should be only use on
74 | * client apps which doesn't call your server. It is less secure than non-PKCE flow but
75 | * can be used if you are unable to safely retrieve your app secret
76 | * @returns {Promise} - Url to send user to for Dropbox API authentication
77 | * returned in a promise
78 | */
79 | getAuthenticationUrl(redirectUri: string, state?: string, authType?: 'token' | 'code', tokenAccessType?: null | 'legacy' | 'offline' | 'online', scope?: Array, includeGrantedScopes?: 'none' | 'user' | 'team', usePKCE?: boolean): Promise;
80 |
81 | /**
82 | * Get the client id
83 | * @returns {String} Client id
84 | */
85 | getClientId(): string;
86 |
87 | /**
88 | * Set the access token used to authenticate requests to the API.
89 | * @param accessToken An access token.
90 | */
91 | setAccessToken(accessToken: string): void;
92 |
93 | /**
94 | * Set the client id, which is used to help gain an access token.
95 | * @param clientId Your app's client ID.
96 | */
97 | setClientId(clientId: string): void;
98 |
99 | /**
100 | * Set the client secret
101 | * @param clientSecret Your app's client secret.
102 | */
103 | setClientSecret(clientSecret: string): void;
104 |
105 | /**
106 | * Sets the refresh token
107 | * @param refreshToken - A refresh token
108 | */
109 | setRefreshToken(refreshToken: string): void;
110 |
111 | /**
112 | * Gets the refresh token
113 | * @returns {String} Refresh token
114 | */
115 | getRefreshToken(): string;
116 |
117 | /**
118 | * Sets the access token's expiration date
119 | * @param accessTokenExpiresAt - new expiration date
120 | */
121 | setAccessTokenExpiresAt(accessTokenExpiresAt: Date): void;
122 |
123 | /**
124 | * Gets the access token's expiration date
125 | * @returns {Date} date of token expiration
126 | */
127 | getAccessTokenExpiresAt(): Date;
128 |
129 | /**
130 | * Sets the code verifier for PKCE flow
131 | * @param {String} codeVerifier - new code verifier
132 | */
133 | setCodeVerifier(codeVerifier: string): void;
134 |
135 | /**
136 | * Gets the code verifier for PKCE flow
137 | * @returns {String} - code verifier for PKCE
138 | */
139 | getCodeVerifier(): string;
140 |
141 | /**
142 | * Checks if a token is needed, can be refreshed and if the token is expired.
143 | * If so, attempts to refresh access token
144 | * @returns {Promise<*>}
145 | */
146 | checkAndRefreshAccessToken(): void;
147 |
148 | /**
149 | * Refreshes the access token using the refresh token, if available
150 | * @arg {List} scope - a subset of scopes from the original
151 | * refresh to acquire with an access token
152 | * @returns {Promise<*>}
153 | */
154 | refreshAccessToken(scope?: Array): void;
155 |
156 | }
157 |
158 | export interface DropboxOptions {
159 | // Select user is only used for team functionality. It specifies which user the team access token should be acting as.
160 | selectUser?: string;
161 | // Select admin is only used by team functionality. It specifies which team admin the team access token should be acting as.
162 | selectAdmin?: string;
163 | // Root path to access other namespaces. Use to access team folders for example
164 | pathRoot?: string;
165 | // The DropboxAuth object used to authenticate requests. If this is set, the remaining parameters will be ignored.
166 | auth?: DropboxAuth | null;
167 | // An access token for making authenticated requests.
168 | accessToken?: string;
169 | // The time at which the access token expires.
170 | accessTokenExpiresAt?: Date;
171 | // A refresh token for retrieving access tokens
172 | refreshToken?: string;
173 | // The client id for your app. Used to create authentication URL.
174 | clientId?: string;
175 | // The client secret for your app. Used for refresh and token exchange.
176 | clientSecret?: string;
177 | // The fetch library for making requests.
178 | fetch?: Function;
179 | // A custom domain to use when making api requests. This should only be used for testing as scaffolding to avoid making network requests.
180 | domain?: string;
181 | // A custom delimiter to use when separating domain subdomain. This should only be used for testing as scaffolding.
182 | domainDelimiter?: string;
183 | // An object (in the form of header: value) designed to set custom headers to use during a request.
184 | customHeaders?: object;
185 | }
186 |
187 | export class DropboxResponseError {
188 | /**
189 | * The response class of HTTP errors from API calls using the Dropbox SDK.
190 | */
191 | constructor(status: number, headers: any, error: T);
192 |
193 | /**
194 | * HTTP Status code of the call
195 | */
196 | status: number;
197 |
198 | /**
199 | * Headers returned from the call. Set as any to support both node and browser.
200 | */
201 | headers: any;
202 |
203 | /**
204 | * Serialized Error of the call
205 | */
206 | error: T;
207 | }
208 |
209 | export class DropboxResponse {
210 | /**
211 | * The response class of all successful API calls using the Dropbox SDK.
212 | */
213 | constructor(status: number, headers: any, result: T);
214 |
215 | /**
216 | * HTTP Status code of the call
217 | */
218 | status: number;
219 |
220 | /**
221 | * Headers returned from the call. Set as any to support both node and browser.
222 | */
223 | headers: any;
224 |
225 | /**
226 | * Serialized Result of the call
227 | */
228 | result: T;
229 | }
230 |
231 | export class Dropbox {
232 | /**
233 | * The Dropbox SDK class that provides methods to read, write and
234 | * create files or folders in a user or team's Dropbox.
235 | */
236 | constructor();
237 |
238 | /**
239 | * The Dropbox SDK class that provides methods to read, write and
240 | * create files or folders in a user or team's Dropbox.
241 | */
242 | constructor(options: DropboxOptions);
243 | /*ROUTES*/
244 | }
245 |
246 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | export { default as Dropbox } from './src/dropbox.js';
2 | export { default as DropboxAuth } from './src/auth.js';
3 | export { DropboxResponse } from './src/response.js';
4 | export { DropboxResponseError } from './src/error.js';
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dropbox",
3 | "version": "10.34.0",
4 | "registry": "npm",
5 | "description": "The Dropbox JavaScript SDK is a lightweight, promise based interface to the Dropbox v2 API that works in both nodejs and browser environments.",
6 | "main": "cjs/index.js",
7 | "browser": "dist/Dropbox-sdk.min.js",
8 | "typings": "types/index",
9 | "types": "types/index",
10 | "module": "es/index.js",
11 | "jsnext:main": "es/index.js",
12 | "scripts": {
13 | "build:es": "cross-env BABEL_ENV=es babel src -d es/src && cross-env BABEL_ENV=es babel lib -d es/lib && cross-env BABEL_ENV=es babel index.js -d es",
14 | "build:cjs": "cross-env BABEL_ENV=commonjs babel src -d cjs/src && cross-env BABEL_ENV=commonjs babel lib -d cjs/lib && cross-env BABEL_ENV=commonjs babel index.js -d cjs",
15 | "build:umd": "cross-env BABEL_ENV=es BUNDLE_TYPE=normal rollup -c -i index.js -o dist/Dropbox-sdk.js -n Dropbox",
16 | "build:umd:min": "cross-env BABEL_ENV=es BUNDLE_TYPE=minified rollup -c -i index.js -o dist/Dropbox-sdk.min.js -n Dropbox",
17 | "build": "npm run build:es && npm run build:cjs && npm run build:umd && npm run build:umd:min",
18 | "lint": "eslint --ext .js,.jsx,.ts .",
19 | "lint-fix": "eslint --fix --ext .js,.jsx,.ts .",
20 | "test": "npm run test:typescript && npm run test:unit",
21 | "test:typescript": "tsc --build test/types",
22 | "test:integration": "mocha --timeout 10000 --require @babel/register test/integration/**/*.js",
23 | "test:unit": "mocha --require @babel/register test/unit/**/*.js",
24 | "test:build": "mocha --timeout 100000 --require @babel/register test/build/*.js",
25 | "report": "nyc report --reporter=lcov --reporter=text",
26 | "clean": "rm -rf dist es cjs",
27 | "generate-docs": "jsdoc -c ./.jsdoc.json",
28 | "coverage:unit": "cross-env BABEL_ENV=coverage nyc --reporter=lcov npm run test:unit",
29 | "coverage:integration": "cross-env BABEL_ENV=coverage nyc --reporter=lcov npm run test:integration"
30 | },
31 | "files": [
32 | "*.md",
33 | "LICENSE",
34 | "index.js",
35 | "src",
36 | "lib",
37 | "types",
38 | "dist",
39 | "es",
40 | "cjs"
41 | ],
42 | "keywords": [
43 | "dropbox",
44 | "files",
45 | "sync",
46 | "sdk",
47 | "client"
48 | ],
49 | "homepage": "https://github.com/dropbox/dropbox-sdk-js#readme",
50 | "repository": {
51 | "type": "git",
52 | "url": "git+https://github.com/dropbox/dropbox-sdk-js.git"
53 | },
54 | "bugs": {
55 | "url": "https://github.com/dropbox/dropbox-sdk-js/issues"
56 | },
57 | "license": "MIT",
58 | "directories": {
59 | "example": "examples",
60 | "test": "test"
61 | },
62 | "engines": {
63 | "node": ">=0.10.3"
64 | },
65 | "contributors": [
66 | "Brad Rogers ",
67 | "Andrew Lawson ",
68 | "James Sidhu ",
69 | "John Vilk ",
70 | "Steve Klebanoff ",
71 | "Bohdan Tereta "
72 | ],
73 | "devDependencies": {
74 | "@babel/cli": "^7.11.6",
75 | "@babel/core": "^7.11.6",
76 | "@babel/preset-env": "^7.11.5",
77 | "@babel/register": "^7.11.5",
78 | "@testing-library/dom": "^7.24.5",
79 | "@types/node": "^14.11.2",
80 | "@types/node-fetch": "^2.5.7",
81 | "@typescript-eslint/eslint-plugin": "^4.0.0",
82 | "@typescript-eslint/parser": "^3.10.1",
83 | "babel-plugin-istanbul": "^6.0.0",
84 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
85 | "chai": "^4.2.0",
86 | "chai-as-promised": "^7.1.1",
87 | "cross-env": "^7.0.2",
88 | "eslint": "^7.9.0",
89 | "eslint-config-airbnb-base": "^14.2.0",
90 | "eslint-plugin-import": "^2.22.0",
91 | "express": "^4.17.1",
92 | "express-urlrewrite": "^1.3.0",
93 | "gh-pages": "^3.1.0",
94 | "ink-docstrap": "^1.3.2",
95 | "jsdoc": "^3.6.6",
96 | "jsdom": "^16.4.0",
97 | "mocha": "^8.1.3",
98 | "nyc": "^15.1.0",
99 | "prompt": "^1.0.0",
100 | "rollup": "^2.28.2",
101 | "rollup-endpoint": "^0.2.2",
102 | "rollup-plugin-babel": "^4.4.0",
103 | "rollup-plugin-terser": "^7.0.2",
104 | "sinon": "^9.0.3",
105 | "typescript": "^4.0.3"
106 | },
107 | "peerDependencies": {
108 | "@types/node-fetch": "^2.5.7"
109 | },
110 | "dependencies": {
111 | "node-fetch": "^2.6.1"
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import babel from 'rollup-plugin-babel';
2 | import { terser } from 'rollup-plugin-terser';
3 |
4 | /**
5 | * We use rollup for the UMD build.
6 | * Rollup is only needed for this because UMD is the
7 | * only build configuration that requires us to bundle
8 | * all assets into a single file.
9 | *
10 | * We also only publish a minified bundle for UMD.
11 | * We use a flag of BUNDLE_TYPE set by cross-env to control
12 | * whether or not we minify the bundle. We use terser to
13 | * do the actual minification and it is only added when the
14 | * BUNDLE_TYPE = minified (BUNDLE_TYPE=normal for basic UMD)
15 | */
16 |
17 | const config = {
18 | output: {
19 | format: 'umd',
20 | sourcemap: (process.env.BUNDLE_TYPE !== 'minified'),
21 | globals: {
22 | crypto: 'crypto',
23 | },
24 | },
25 | external: ['es6-promise/auto', 'crypto'],
26 | plugins: [
27 | babel(),
28 | ],
29 | };
30 |
31 | if (process.env.BUNDLE_TYPE === 'minified') {
32 | config.plugins.push(
33 | terser({
34 | compress: {
35 | pure_getters: true,
36 | unsafe: true,
37 | unsafe_comps: true,
38 | warnings: false,
39 | },
40 | }),
41 | );
42 | }
43 |
44 | export default config;
45 |
--------------------------------------------------------------------------------
/scripts/release_note_generator.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | last_version=$(git tag --sort v:refname | tail -n 2 | head -n 1)
4 | echo "Getting commit history since $last_version"
5 | num_commits=$(git rev-list --count $last_version..HEAD)
6 | echo "Found $num_commits commits since last revision"
7 | git_log=$(git log -n $num_commits --pretty="format:* %s %n")
8 | linked_log=$(echo "Release Notes: \n\n$git_log" | sed -e 's/#\([0-9]*\)/[#\1](https:\/\/github.com\/dropbox\/dropbox-sdk-js\/pull\/\1)/g')
9 | echo "\n\n$linked_log"
--------------------------------------------------------------------------------
/scripts/update_version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ -z $1 ]; then
4 | echo "error: $0 needs a version number as argument.";
5 | exit 1
6 | else
7 | set -ex
8 | NEW_VERSION=$1
9 |
10 | git checkout main
11 | git reset --hard HEAD
12 |
13 | git tag "v${NEW_VERSION}" -m "${NEW_VERSION} release"
14 |
15 | git push origin
16 | git push origin --tags
17 | fi
--------------------------------------------------------------------------------
/src/auth.js:
--------------------------------------------------------------------------------
1 | import {
2 | getTokenExpiresAtDate,
3 | isBrowserEnv,
4 | createBrowserSafeString,
5 | OAuth2AuthorizationUrl,
6 | OAuth2TokenUrl,
7 | isWorkerEnv,
8 | } from './utils.js';
9 | import { parseResponse } from './response.js';
10 |
11 | let fetch;
12 | let crypto;
13 | let Encoder;
14 |
15 | // Expiration is 300 seconds but needs to be in milliseconds for Date object
16 | const TokenExpirationBuffer = 300 * 1000;
17 | const PKCELength = 128;
18 | const TokenAccessTypes = ['legacy', 'offline', 'online'];
19 | const GrantTypes = ['code', 'token'];
20 | const IncludeGrantedScopes = ['none', 'user', 'team'];
21 |
22 | /**
23 | * @class DropboxAuth
24 | * @classdesc The DropboxAuth class that provides methods to manage, acquire, and refresh tokens.
25 | * @arg {Object} options
26 | * @arg {Function} [options.fetch] - fetch library for making requests.
27 | * @arg {String} [options.accessToken] - An access token for making authenticated
28 | * requests.
29 | * @arg {Date} [options.AccessTokenExpiresAt] - Date of the current access token's
30 | * expiration (if available)
31 | * @arg {String} [options.refreshToken] - A refresh token for retrieving access tokens
32 | * @arg {String} [options.clientId] - The client id for your app. Used to create
33 | * authentication URL.
34 | * @arg {String} [options.clientSecret] - The client secret for your app. Used to create
35 | * authentication URL and refresh access tokens.
36 | * @arg {String} [options.domain] - A custom domain to use when making api requests. This
37 | * should only be used for testing as scaffolding to avoid making network requests.
38 | * @arg {String} [options.domainDelimiter] - A custom delimiter to use when separating domain from
39 | * subdomain. This should only be used for testing as scaffolding.
40 | * @arg {Object} [options.customHeaders] - An object (in the form of header: value) designed to set
41 | * custom headers to use during a request.
42 | * @arg {Boolean} [options.dataOnBody] - Whether request data is sent on body or as URL params.
43 | * Defaults to false.
44 | */
45 | export default class DropboxAuth {
46 | constructor(options) {
47 | options = options || {};
48 |
49 | if (isBrowserEnv()) {
50 | fetch = window.fetch.bind(window);
51 | crypto = window.crypto || window.msCrypto; // for IE11
52 | } else if (isWorkerEnv()) {
53 | /* eslint-disable no-restricted-globals */
54 | fetch = self.fetch.bind(self);
55 | crypto = self.crypto;
56 | /* eslint-enable no-restricted-globals */
57 | } else {
58 | fetch = require('node-fetch'); // eslint-disable-line global-require
59 | crypto = require('crypto'); // eslint-disable-line global-require
60 | }
61 |
62 | if (typeof TextEncoder === 'undefined') {
63 | Encoder = require('util').TextEncoder; // eslint-disable-line global-require
64 | } else {
65 | Encoder = TextEncoder;
66 | }
67 |
68 | this.fetch = options.fetch || fetch;
69 | this.accessToken = options.accessToken;
70 | this.accessTokenExpiresAt = options.accessTokenExpiresAt;
71 | this.refreshToken = options.refreshToken;
72 | this.clientId = options.clientId;
73 | this.clientSecret = options.clientSecret;
74 |
75 | this.domain = options.domain;
76 | this.domainDelimiter = options.domainDelimiter;
77 | this.customHeaders = options.customHeaders;
78 | this.dataOnBody = options.dataOnBody;
79 | }
80 |
81 | /**
82 | * Set the access token used to authenticate requests to the API.
83 | * @arg {String} accessToken - An access token
84 | * @returns {undefined}
85 | */
86 | setAccessToken(accessToken) {
87 | this.accessToken = accessToken;
88 | }
89 |
90 | /**
91 | * Get the access token
92 | * @returns {String} Access token
93 | */
94 | getAccessToken() {
95 | return this.accessToken;
96 | }
97 |
98 | /**
99 | * Set the client id, which is used to help gain an access token.
100 | * @arg {String} clientId - Your apps client id
101 | * @returns {undefined}
102 | */
103 | setClientId(clientId) {
104 | this.clientId = clientId;
105 | }
106 |
107 | /**
108 | * Get the client id
109 | * @returns {String} Client id
110 | */
111 | getClientId() {
112 | return this.clientId;
113 | }
114 |
115 | /**
116 | * Set the client secret
117 | * @arg {String} clientSecret - Your app's client secret
118 | * @returns {undefined}
119 | */
120 | setClientSecret(clientSecret) {
121 | this.clientSecret = clientSecret;
122 | }
123 |
124 | /**
125 | * Get the client secret
126 | * @returns {String} Client secret
127 | */
128 | getClientSecret() {
129 | return this.clientSecret;
130 | }
131 |
132 | /**
133 | * Gets the refresh token
134 | * @returns {String} Refresh token
135 | */
136 | getRefreshToken() {
137 | return this.refreshToken;
138 | }
139 |
140 | /**
141 | * Sets the refresh token
142 | * @param refreshToken - A refresh token
143 | */
144 | setRefreshToken(refreshToken) {
145 | this.refreshToken = refreshToken;
146 | }
147 |
148 | /**
149 | * Gets the access token's expiration date
150 | * @returns {Date} date of token expiration
151 | */
152 | getAccessTokenExpiresAt() {
153 | return this.accessTokenExpiresAt;
154 | }
155 |
156 | /**
157 | * Sets the access token's expiration date
158 | * @param accessTokenExpiresAt - new expiration date
159 | */
160 | setAccessTokenExpiresAt(accessTokenExpiresAt) {
161 | this.accessTokenExpiresAt = accessTokenExpiresAt;
162 | }
163 |
164 | /**
165 | * Sets the code verifier for PKCE flow
166 | * @param {String} codeVerifier - new code verifier
167 | */
168 | setCodeVerifier(codeVerifier) {
169 | this.codeVerifier = codeVerifier;
170 | }
171 |
172 | /**
173 | * Gets the code verifier for PKCE flow
174 | * @returns {String} - code verifier for PKCE
175 | */
176 | getCodeVerifier() {
177 | return this.codeVerifier;
178 | }
179 |
180 | generateCodeChallenge() {
181 | const encoder = new Encoder();
182 | const codeData = encoder.encode(this.codeVerifier);
183 | let codeChallenge;
184 | if (isBrowserEnv() || isWorkerEnv()) {
185 | return crypto.subtle.digest('SHA-256', codeData)
186 | .then((digestedHash) => {
187 | const base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(digestedHash)));
188 | codeChallenge = createBrowserSafeString(base64String).substr(0, 128);
189 | this.codeChallenge = codeChallenge;
190 | });
191 | }
192 | const digestedHash = crypto.createHash('sha256').update(codeData).digest();
193 | codeChallenge = createBrowserSafeString(digestedHash);
194 | this.codeChallenge = codeChallenge;
195 | return Promise.resolve();
196 | }
197 |
198 | generatePKCECodes() {
199 | let codeVerifier;
200 | if (isBrowserEnv() || isWorkerEnv()) {
201 | const array = new Uint8Array(PKCELength);
202 | const randomValueArray = crypto.getRandomValues(array);
203 | const base64String = btoa(randomValueArray);
204 | codeVerifier = createBrowserSafeString(base64String).substr(0, 128);
205 | } else {
206 | const randomBytes = crypto.randomBytes(PKCELength);
207 | codeVerifier = createBrowserSafeString(randomBytes).substr(0, 128);
208 | }
209 | this.codeVerifier = codeVerifier;
210 |
211 | return this.generateCodeChallenge();
212 | }
213 |
214 | /**
215 | * Get a URL that can be used to authenticate users for the Dropbox API.
216 | * @arg {String} redirectUri - A URL to redirect the user to after
217 | * authenticating. This must be added to your app through the admin interface.
218 | * @arg {String} [state] - State that will be returned in the redirect URL to help
219 | * prevent cross site scripting attacks.
220 | * @arg {String} [authType] - auth type, defaults to 'token', other option is 'code'
221 | * @arg {String} [tokenAccessType] - type of token to request. From the following:
222 | * null - creates a token with the app default (either legacy or online)
223 | * legacy - creates one long-lived token with no expiration
224 | * online - create one short-lived token with an expiration
225 | * offline - create one short-lived token with an expiration with a refresh token
226 | * @arg {Array} [scope] - scopes to request for the grant
227 | * @arg {String} [includeGrantedScopes] - whether or not to include previously granted scopes.
228 | * From the following:
229 | * user - include user scopes in the grant
230 | * team - include team scopes in the grant
231 | * Note: if this user has never linked the app, include_granted_scopes must be None
232 | * @arg {boolean} [usePKCE] - Whether or not to use Sha256 based PKCE. PKCE should be only use
233 | * on client apps which doesn't call your server. It is less secure than non-PKCE flow but
234 | * can be used if you are unable to safely retrieve your app secret
235 | * @returns {Promise} - Url to send user to for Dropbox API authentication
236 | * returned in a promise
237 | */
238 | getAuthenticationUrl(redirectUri, state, authType = 'token', tokenAccessType = null, scope = null, includeGrantedScopes = 'none', usePKCE = false) {
239 | const clientId = this.getClientId();
240 | const baseUrl = OAuth2AuthorizationUrl(this.domain);
241 |
242 | if (!clientId) {
243 | throw new Error('A client id is required. You can set the client id using .setClientId().');
244 | }
245 | if (authType !== 'code' && !redirectUri) {
246 | throw new Error('A redirect uri is required.');
247 | }
248 | if (!GrantTypes.includes(authType)) {
249 | throw new Error('Authorization type must be code or token');
250 | }
251 | if (tokenAccessType && !TokenAccessTypes.includes(tokenAccessType)) {
252 | throw new Error('Token Access Type must be legacy, offline, or online');
253 | }
254 | if (scope && !(scope instanceof Array)) {
255 | throw new Error('Scope must be an array of strings');
256 | }
257 | if (!IncludeGrantedScopes.includes(includeGrantedScopes)) {
258 | throw new Error('includeGrantedScopes must be none, user, or team');
259 | }
260 |
261 | let authUrl;
262 | if (authType === 'code') {
263 | authUrl = `${baseUrl}?response_type=code&client_id=${clientId}`;
264 | } else {
265 | authUrl = `${baseUrl}?response_type=token&client_id=${clientId}`;
266 | }
267 |
268 | if (redirectUri) {
269 | authUrl += `&redirect_uri=${redirectUri}`;
270 | }
271 | if (state) {
272 | authUrl += `&state=${state}`;
273 | }
274 | if (tokenAccessType) {
275 | authUrl += `&token_access_type=${tokenAccessType}`;
276 | }
277 | if (scope) {
278 | authUrl += `&scope=${scope.join(' ')}`;
279 | }
280 | if (includeGrantedScopes !== 'none') {
281 | authUrl += `&include_granted_scopes=${includeGrantedScopes}`;
282 | }
283 | if (usePKCE) {
284 | return this.generatePKCECodes()
285 | .then(() => {
286 | authUrl += '&code_challenge_method=S256';
287 | authUrl += `&code_challenge=${this.codeChallenge}`;
288 | return authUrl;
289 | });
290 | }
291 | return Promise.resolve(authUrl);
292 | }
293 |
294 | /**
295 | * Get an OAuth2 access token from an OAuth2 Code.
296 | * @arg {String} redirectUri - A URL to redirect the user to after
297 | * authenticating. This must be added to your app through the admin interface.
298 | * @arg {String} code - An OAuth2 code.
299 | * @returns {Object} An object containing the token and related info (if applicable)
300 | */
301 | getAccessTokenFromCode(redirectUri, code) {
302 | const clientId = this.getClientId();
303 | const clientSecret = this.getClientSecret();
304 |
305 | if (!clientId) {
306 | throw new Error('A client id is required. You can set the client id using .setClientId().');
307 | }
308 | let path = OAuth2TokenUrl(this.domain, this.domainDelimiter);
309 | path += '?grant_type=authorization_code';
310 | path += `&code=${code}`;
311 | path += `&client_id=${clientId}`;
312 |
313 | if (clientSecret) {
314 | path += `&client_secret=${clientSecret}`;
315 | } else {
316 | if (!this.codeVerifier) {
317 | throw new Error('You must use PKCE when generating the authorization URL to not include a client secret');
318 | }
319 | path += `&code_verifier=${this.codeVerifier}`;
320 | }
321 | if (redirectUri) {
322 | path += `&redirect_uri=${redirectUri}`;
323 | }
324 |
325 | const fetchOptions = {
326 | method: 'POST',
327 | headers: {
328 | 'Content-Type': 'application/x-www-form-urlencoded',
329 | },
330 | };
331 | return this.fetch(path, fetchOptions)
332 | .then((res) => parseResponse(res));
333 | }
334 |
335 | /**
336 | * Checks if a token is needed, can be refreshed and if the token is expired.
337 | * If so, attempts to refresh access token
338 | * @returns {Promise<*>}
339 | */
340 | checkAndRefreshAccessToken() {
341 | const canRefresh = this.getRefreshToken() && this.getClientId();
342 | const needsRefresh = !this.getAccessTokenExpiresAt()
343 | || (new Date(Date.now() + TokenExpirationBuffer)) >= this.getAccessTokenExpiresAt();
344 | const needsToken = !this.getAccessToken();
345 | if ((needsRefresh || needsToken) && canRefresh) {
346 | return this.refreshAccessToken();
347 | }
348 | return Promise.resolve();
349 | }
350 |
351 | /**
352 | * Refreshes the access token using the refresh token, if available
353 | * @arg {Array} scope - a subset of scopes from the original
354 | * refresh to acquire with an access token
355 | * @returns {Promise<*>}
356 | */
357 | refreshAccessToken(scope = null) {
358 | const clientId = this.getClientId();
359 | const clientSecret = this.getClientSecret();
360 |
361 | if (!clientId) {
362 | throw new Error('A client id is required. You can set the client id using .setClientId().');
363 | }
364 | if (scope && !(scope instanceof Array)) {
365 | throw new Error('Scope must be an array of strings');
366 | }
367 |
368 | let refreshUrl = OAuth2TokenUrl(this.domain, this.domainDelimiter);
369 | const fetchOptions = {
370 | headers: { 'Content-Type': 'application/json' },
371 | method: 'POST',
372 | };
373 |
374 | if (this.dataOnBody) {
375 | const body = { grant_type: 'refresh_token', client_id: clientId, refresh_token: this.getRefreshToken() };
376 |
377 | if (clientSecret) {
378 | body.client_secret = clientSecret;
379 | }
380 | if (scope) {
381 | body.scope = scope.join(' ');
382 | }
383 |
384 | fetchOptions.body = body;
385 | } else {
386 | refreshUrl += `?grant_type=refresh_token&refresh_token=${this.getRefreshToken()}`;
387 | refreshUrl += `&client_id=${clientId}`;
388 | if (clientSecret) {
389 | refreshUrl += `&client_secret=${clientSecret}`;
390 | }
391 | if (scope) {
392 | refreshUrl += `&scope=${scope.join(' ')}`;
393 | }
394 | }
395 |
396 | return this.fetch(refreshUrl, fetchOptions)
397 | .then((res) => parseResponse(res))
398 | .then((res) => {
399 | this.setAccessToken(res.result.access_token);
400 | this.setAccessTokenExpiresAt(getTokenExpiresAtDate(res.result.expires_in));
401 | });
402 | }
403 | }
404 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | export const RPC = 'rpc';
2 | export const UPLOAD = 'upload';
3 | export const DOWNLOAD = 'download';
4 |
5 | export const APP_AUTH = 'app';
6 | export const USER_AUTH = 'user';
7 | export const TEAM_AUTH = 'team';
8 | export const NO_AUTH = 'noauth';
9 | export const COOKIE = 'cookie';
10 |
11 | export const DEFAULT_API_DOMAIN = 'dropboxapi.com';
12 | export const DEFAULT_DOMAIN = 'dropbox.com';
13 |
14 | export const TEST_DOMAIN_MAPPINGS = {
15 | api: 'api',
16 | notify: 'bolt',
17 | content: 'api-content',
18 | };
19 |
--------------------------------------------------------------------------------
/src/dropbox.js:
--------------------------------------------------------------------------------
1 | import {
2 | UPLOAD,
3 | DOWNLOAD,
4 | RPC,
5 | APP_AUTH,
6 | TEAM_AUTH,
7 | USER_AUTH,
8 | NO_AUTH,
9 | COOKIE,
10 | } from './constants.js';
11 | import { routes } from '../lib/routes.js';
12 | import DropboxAuth from './auth.js';
13 | import { baseApiUrl, httpHeaderSafeJson } from './utils.js';
14 | import { parseDownloadResponse, parseResponse } from './response.js';
15 |
16 | const b64 = typeof btoa === 'undefined'
17 | ? (str) => Buffer.from(str).toString('base64')
18 | : btoa;
19 |
20 | /**
21 | * @class Dropbox
22 | * @classdesc The Dropbox SDK class that provides methods to read, write and
23 | * create files or folders in a user or team's Dropbox.
24 | * @arg {Object} options
25 | * @arg {Function} [options.fetch] - fetch library for making requests.
26 | * @arg {String} [options.selectUser] - Select user is only used for team functionality.
27 | * It specifies which user the team access token should be acting as.
28 | * @arg {String} [options.pathRoot] - root path to access other namespaces
29 | * Use to access team folders for example
30 | * @arg {String} [options.selectAdmin] - Select admin is only used by team functionality.
31 | * It specifies which team admin the team access token should be acting as.
32 | * @arg {DropboxAuth} [options.auth] - The DropboxAuth object used to authenticate requests.
33 | * If this is set, the remaining parameters will be ignored.
34 | * @arg {String} [options.accessToken] - An access token for making authenticated
35 | * requests.
36 | * @arg {Date} [options.accessTokenExpiresAt] - Date of the current access token's
37 | * expiration (if available)
38 | * @arg {String} [options.refreshToken] - A refresh token for retrieving access tokens
39 | * @arg {String} [options.clientId] - The client id for your app. Used to create
40 | * authentication URL.
41 | * @arg {String} [options.clientSecret] - The client secret for your app. Used to create
42 | * authentication URL and refresh access tokens.
43 | * @arg {String} [options.domain] - A custom domain to use when making api requests. This
44 | * should only be used for testing as scaffolding to avoid making network requests.
45 | * @arg {String} [options.domainDelimiter] - A custom delimiter to use when separating domain from
46 | * subdomain. This should only be used for testing as scaffolding.
47 | * @arg {Object} [options.customHeaders] - An object (in the form of header: value) designed to set
48 | * custom headers to use during a request.
49 | */
50 | export default class Dropbox {
51 | constructor(options) {
52 | options = options || {};
53 |
54 | if (options.auth) {
55 | this.auth = options.auth;
56 | } else {
57 | this.auth = new DropboxAuth(options);
58 | }
59 |
60 | this.fetch = options.fetch || this.auth.fetch;
61 | this.selectUser = options.selectUser;
62 | this.selectAdmin = options.selectAdmin;
63 | this.pathRoot = options.pathRoot;
64 |
65 | this.domain = options.domain || this.auth.domain;
66 | this.domainDelimiter = options.domainDelimiter || this.auth.domainDelimiter;
67 | this.customHeaders = options.customHeaders || this.auth.customHeaders;
68 |
69 | Object.assign(this, routes);
70 | }
71 |
72 | request(path, args, auth, host, style) {
73 | // scope is provided after "style", but unused in requests, so it's not in parameters
74 | switch (style) {
75 | case RPC:
76 | return this.rpcRequest(path, args, auth, host);
77 | case DOWNLOAD:
78 | return this.downloadRequest(path, args, auth, host);
79 | case UPLOAD:
80 | return this.uploadRequest(path, args, auth, host);
81 | default:
82 | throw new Error(`Invalid request style: ${style}`);
83 | }
84 | }
85 |
86 | rpcRequest(path, body, auth, host) {
87 | return this.auth.checkAndRefreshAccessToken()
88 | .then(() => {
89 | const fetchOptions = {
90 | method: 'POST',
91 | body: (body) ? JSON.stringify(body) : null,
92 | headers: {},
93 | };
94 |
95 | if (body) {
96 | fetchOptions.headers['Content-Type'] = 'application/json';
97 | }
98 |
99 | this.setAuthHeaders(auth, fetchOptions);
100 | this.setCommonHeaders(fetchOptions);
101 |
102 | return fetchOptions;
103 | })
104 | .then((fetchOptions) => this.fetch(
105 | baseApiUrl(host, this.domain, this.domainDelimiter) + path,
106 | fetchOptions,
107 | ))
108 | .then((res) => parseResponse(res));
109 | }
110 |
111 | downloadRequest(path, args, auth, host) {
112 | return this.auth.checkAndRefreshAccessToken()
113 | .then(() => {
114 | const fetchOptions = {
115 | method: 'POST',
116 | headers: {
117 | 'Dropbox-API-Arg': httpHeaderSafeJson(args),
118 | },
119 | };
120 |
121 | this.setAuthHeaders(auth, fetchOptions);
122 | this.setCommonHeaders(fetchOptions);
123 |
124 | return fetchOptions;
125 | })
126 | .then((fetchOptions) => this.fetch(
127 | baseApiUrl(host, this.domain, this.domainDelimiter) + path,
128 | fetchOptions,
129 | ))
130 | .then((res) => parseDownloadResponse(res));
131 | }
132 |
133 | uploadRequest(path, args, auth, host) {
134 | return this.auth.checkAndRefreshAccessToken()
135 | .then(() => {
136 | const { contents } = args;
137 | delete args.contents;
138 |
139 | const fetchOptions = {
140 | body: contents,
141 | method: 'POST',
142 | headers: {
143 | 'Content-Type': 'application/octet-stream',
144 | 'Dropbox-API-Arg': httpHeaderSafeJson(args),
145 | },
146 | };
147 |
148 | this.setAuthHeaders(auth, fetchOptions);
149 | this.setCommonHeaders(fetchOptions);
150 |
151 | return fetchOptions;
152 | })
153 | .then((fetchOptions) => this.fetch(
154 | baseApiUrl(host, this.domain, this.domainDelimiter) + path,
155 | fetchOptions,
156 | ))
157 | .then((res) => parseResponse(res));
158 | }
159 |
160 | setAuthHeaders(auth, fetchOptions) {
161 | // checks for multiauth and assigns auth based on priority to create header in switch case
162 | if (auth.split(',').length > 1) {
163 | const authTypes = auth.replace(' ', '').split(',');
164 | if (authTypes.includes(USER_AUTH) && this.auth.getAccessToken()) {
165 | auth = USER_AUTH;
166 | } else if (authTypes.includes(TEAM_AUTH) && this.auth.getAccessToken()) {
167 | auth = TEAM_AUTH;
168 | } else if (authTypes.includes(APP_AUTH)) {
169 | auth = APP_AUTH;
170 | }
171 | }
172 |
173 | switch (auth) {
174 | case APP_AUTH:
175 | if (this.auth.clientId && this.auth.clientSecret) {
176 | const authHeader = b64(`${this.auth.clientId}:${this.auth.clientSecret}`);
177 | fetchOptions.headers.Authorization = `Basic ${authHeader}`;
178 | }
179 | break;
180 | case TEAM_AUTH:
181 | case USER_AUTH:
182 | if (this.auth.getAccessToken()) {
183 | fetchOptions.headers.Authorization = `Bearer ${this.auth.getAccessToken()}`;
184 | }
185 | break;
186 | case NO_AUTH:
187 | case COOKIE:
188 | break;
189 | default:
190 | throw new Error(`Unhandled auth type: ${auth}`);
191 | }
192 | }
193 |
194 | setCommonHeaders(options) {
195 | if (this.selectUser) {
196 | options.headers['Dropbox-API-Select-User'] = this.selectUser;
197 | }
198 | if (this.selectAdmin) {
199 | options.headers['Dropbox-API-Select-Admin'] = this.selectAdmin;
200 | }
201 | if (this.pathRoot) {
202 | options.headers['Dropbox-API-Path-Root'] = this.pathRoot;
203 | }
204 | if (this.customHeaders) {
205 | const headerKeys = Object.keys(this.customHeaders);
206 | headerKeys.forEach((header) => {
207 | options.headers[header] = this.customHeaders[header];
208 | });
209 | }
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/src/error.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The response class of HTTP errors from API calls using the Dropbox SDK.
3 | * @class DropboxResponseError
4 | * @classdesc The response class of HTTP errors from API calls using the Dropbox SDK.
5 | * @arg {number} status - HTTP Status code of the call
6 | * @arg {Object} headers - Headers returned from the call
7 | * @arg {Object} error - Serialized Error of the call
8 | */
9 | export class DropboxResponseError extends Error {
10 | constructor(status, headers, error) {
11 | super(`Response failed with a ${status} code`);
12 | this.name = 'DropboxResponseError';
13 | this.status = status;
14 | this.headers = headers;
15 | this.error = error;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/response.js:
--------------------------------------------------------------------------------
1 | import { isWindowOrWorker } from './utils.js';
2 | import { DropboxResponseError } from './error.js';
3 |
4 | export class DropboxResponse {
5 | constructor(status, headers, result) {
6 | this.status = status;
7 | this.headers = headers;
8 | this.result = result;
9 | }
10 | }
11 |
12 | function throwAsError(res) {
13 | return res.text()
14 | .then((data) => {
15 | let errorObject;
16 | try {
17 | errorObject = JSON.parse(data);
18 | } catch (error) {
19 | errorObject = data;
20 | }
21 |
22 | throw new DropboxResponseError(res.status, res.headers, errorObject);
23 | });
24 | }
25 |
26 | export function parseResponse(res) {
27 | if (!res.ok) {
28 | return throwAsError(res);
29 | }
30 | return res.text()
31 | .then((data) => {
32 | let responseObject;
33 | try {
34 | responseObject = JSON.parse(data);
35 | } catch (error) {
36 | responseObject = data;
37 | }
38 |
39 | return new DropboxResponse(res.status, res.headers, responseObject);
40 | });
41 | }
42 |
43 | export function parseDownloadResponse(res) {
44 | if (!res.ok) {
45 | return throwAsError(res);
46 | }
47 | return new Promise((resolve) => {
48 | if (isWindowOrWorker()) {
49 | res.blob().then((data) => resolve(data));
50 | } else {
51 | res.buffer().then((data) => resolve(data));
52 | }
53 | }).then((data) => {
54 | const result = JSON.parse(res.headers.get('dropbox-api-result'));
55 |
56 | if (isWindowOrWorker()) {
57 | result.fileBlob = data;
58 | } else {
59 | result.fileBinary = data;
60 | }
61 |
62 | return new DropboxResponse(res.status, res.headers, result);
63 | });
64 | }
65 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | import { DEFAULT_API_DOMAIN, DEFAULT_DOMAIN, TEST_DOMAIN_MAPPINGS } from './constants';
2 |
3 | function getSafeUnicode(c) {
4 | const unicode = `000${c.charCodeAt(0).toString(16)}`.slice(-4);
5 | return `\\u${unicode}`;
6 | }
7 |
8 | export const baseApiUrl = (subdomain, domain = DEFAULT_API_DOMAIN, domainDelimiter = '.') => {
9 | if (!domainDelimiter) {
10 | return `https://${domain}/2/`;
11 | }
12 | if (domain !== DEFAULT_API_DOMAIN && TEST_DOMAIN_MAPPINGS[subdomain] !== undefined) {
13 | subdomain = TEST_DOMAIN_MAPPINGS[subdomain];
14 | domainDelimiter = '-';
15 | }
16 | return `https://${subdomain}${domainDelimiter}${domain}/2/`;
17 | };
18 | export const OAuth2AuthorizationUrl = (domain = DEFAULT_DOMAIN) => {
19 | if (domain !== DEFAULT_DOMAIN) {
20 | domain = `meta-${domain}`;
21 | }
22 | return `https://${domain}/oauth2/authorize`;
23 | };
24 | export const OAuth2TokenUrl = (domain = DEFAULT_API_DOMAIN, domainDelimiter = '.') => {
25 | let subdomain = 'api';
26 | if (domain !== DEFAULT_API_DOMAIN) {
27 | subdomain = TEST_DOMAIN_MAPPINGS[subdomain];
28 | domainDelimiter = '-';
29 | }
30 | return `https://${subdomain}${domainDelimiter}${domain}/oauth2/token`;
31 | };
32 |
33 | // source https://www.dropboxforum.com/t5/API-support/HTTP-header-quot-Dropbox-API-Arg-quot-could-not-decode-input-as/m-p/173823/highlight/true#M6786
34 | export function httpHeaderSafeJson(args) {
35 | return JSON.stringify(args).replace(/[\u007f-\uffff]/g, getSafeUnicode);
36 | }
37 |
38 | export function getTokenExpiresAtDate(expiresIn) {
39 | return new Date(Date.now() + (expiresIn * 1000));
40 | }
41 |
42 | /* global WorkerGlobalScope */
43 | export function isWindowOrWorker() {
44 | return (
45 | (
46 | typeof WorkerGlobalScope !== 'undefined'
47 | && self instanceof WorkerGlobalScope // eslint-disable-line no-restricted-globals
48 | )
49 | || (
50 | typeof module === 'undefined'
51 | || typeof window !== 'undefined'
52 | )
53 | );
54 | }
55 |
56 | export function isBrowserEnv() {
57 | return typeof window !== 'undefined';
58 | }
59 |
60 | export function isWorkerEnv() {
61 | return typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope; // eslint-disable-line no-restricted-globals
62 | }
63 |
64 | export function createBrowserSafeString(toBeConverted) {
65 | const convertedString = toBeConverted.toString('base64')
66 | .replace(/\+/g, '-')
67 | .replace(/\//g, '_')
68 | .replace(/=/g, '');
69 | return convertedString;
70 | }
71 |
--------------------------------------------------------------------------------
/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "rules": {
6 | "no-restricted-syntax": 0,
7 | "no-console": 0,
8 | "no-unused-vars": 0
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/test/build/browser.js:
--------------------------------------------------------------------------------
1 | import { JSDOM } from 'jsdom';
2 | import { fireEvent } from '@testing-library/dom';
3 | import chai from 'chai';
4 | import fs from 'fs';
5 | import path from 'path';
6 |
7 | const SDK_IMPORT_HOLDER = 'sdkscript';
8 |
9 | const setImport = (rawHTML, importPath) => rawHTML.replace(SDK_IMPORT_HOLDER, `file://${importPath}`);
10 |
11 | const executeTest = (testContainer) => {
12 | const children = testContainer.childNodes;
13 | const result = children[1];
14 | chai.assert.equal(result.localName, 'h1', 'Test setup');
15 |
16 | const execute = children[3];
17 | chai.assert.equal(execute.localName, 'button', 'Test setup');
18 |
19 | fireEvent.click(execute);
20 | return result.innerHTML;
21 | };
22 |
23 | describe('Browser Definitions', () => {
24 | describe('ES Build', () => {
25 | let html;
26 | let dom;
27 | // let document;
28 |
29 | before((done) => {
30 | const importPath = path.resolve(__dirname, '../../es/index.js');
31 | html = fs.readFileSync(path.resolve(__dirname, './browser/es.html'), 'utf8');
32 | html = setImport(html, importPath);
33 | done();
34 | });
35 |
36 | beforeEach((done) => {
37 | // Constructing a new JSDOM with this option is the key
38 | // to getting the code in the script tag to execute.
39 | // This is indeed dangerous and should only be done with trusted content.
40 | // https://github.com/jsdom/jsdom#executing-scripts
41 | dom = new JSDOM(html, {
42 | runScripts: 'dangerously',
43 | resources: 'usable',
44 | });
45 | dom.window.onload = () => {
46 | // document = dom.window.document;
47 | done();
48 | };
49 | });
50 |
51 | // Broken until JSDOM supports
5 |
6 |
7 |
8 |
9 |