diff --git a/.dockerignore b/.dockerignore index 5fbe308e265b3..eb0fd3526d9e8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -21,4 +21,3 @@ Dockerfile /apps/*/dist/ /apps/*/types/ /packages/*/dist/ -/packages/*/types/ diff --git a/.gitignore b/.gitignore index 7d8c1858a4436..737e0c3416325 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,3 @@ cached-requests.json /apps/*/dist/ /apps/*/types/ /packages/*/dist/ -# Redundant after https://github.com/Automattic/wp-calypso/pull/39173 -# Safe to remove after some time has passed -/packages/*/types/ diff --git a/packages/data-stores/global.d.ts b/packages/data-stores/global.d.ts deleted file mode 100644 index 48339e4e8e18b..0000000000000 --- a/packages/data-stores/global.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * External dependencies - */ -declare module 'wpcom-proxy-request' { - type WpcomRequestParams = import('./src/utils/wpcom-wrapper').WpcomRequestParams; - export function reloadProxy(): void; - export default function wpcomProxyRequest( - params: WpcomRequestParams, - callback: Function - ): XMLHttpRequest; -} diff --git a/packages/data-stores/src/auth/controls.ts b/packages/data-stores/src/auth/controls.ts index 07ba1defb4706..e2c4dc51fac28 100644 --- a/packages/data-stores/src/auth/controls.ts +++ b/packages/data-stores/src/auth/controls.ts @@ -3,15 +3,19 @@ */ import { createRegistryControl } from '@wordpress/data'; import { stringify } from 'qs'; +import wpcomRequest, { requestAllBlogsAccess } from 'wpcom-proxy-request'; /** * Internal dependencies */ -import { wpcomRequest, WpcomClientCredentials } from '../utils'; import { FetchAuthOptionsAction, FetchWpLoginAction } from './actions'; import { STORE_KEY } from './constants'; +import { WpcomClientCredentials } from '../shared-types'; export function createControls( clientCreds: WpcomClientCredentials ) { + requestAllBlogsAccess().catch( () => { + throw new Error( 'Could not get all blog access.' ); + } ); return { SELECT_USERNAME_OR_EMAIL: createRegistryControl( registry => () => { return registry.select( STORE_KEY ).getUsernameOrEmail(); diff --git a/packages/data-stores/src/auth/index.ts b/packages/data-stores/src/auth/index.ts index f6747cf760b47..1a8b6f17b091a 100644 --- a/packages/data-stores/src/auth/index.ts +++ b/packages/data-stores/src/auth/index.ts @@ -12,7 +12,7 @@ import * as actions from './actions'; import { createControls } from './controls'; import * as selectors from './selectors'; import { DispatchFromMap, SelectFromMap } from '../mapped-types'; -import { WpcomClientCredentials } from '../utils'; +import { WpcomClientCredentials } from '../shared-types'; export * from './types'; export { State }; diff --git a/packages/data-stores/src/auth/test/controls.ts b/packages/data-stores/src/auth/test/controls.ts index 3e5aaaa734939..0d0fb1b627799 100644 --- a/packages/data-stores/src/auth/test/controls.ts +++ b/packages/data-stores/src/auth/test/controls.ts @@ -6,18 +6,22 @@ * @jest-environment jsdom */ +/** + * External dependencies + */ +import wpcomRequest from 'wpcom-proxy-request'; + /** * Internal dependencies */ -import { wpcomRequest as wpcomRequestOriginal } from '../../utils'; import { createControls } from '../controls'; -jest.mock( '../../utils', () => ( { - wpcomRequest: jest.fn(), +jest.mock( 'wpcom-proxy-request', () => ( { + __esModule: true, + default: jest.fn(), + requestAllBlogsAccess: jest.fn( () => Promise.resolve() ), } ) ); -const wpcomRequest: jest.Mock = wpcomRequestOriginal as any; - const { FETCH_AUTH_OPTIONS } = createControls( { client_id: '', client_secret: '' } ); describe( 'FETCH_AUTH_OPTIONS', () => { @@ -26,7 +30,7 @@ describe( 'FETCH_AUTH_OPTIONS', () => { success: true, data: { token_links: [] }, }; - wpcomRequest.mockResolvedValue( apiResponse ); + ( wpcomRequest as jest.Mock ).mockResolvedValue( apiResponse ); const result = await FETCH_AUTH_OPTIONS( { type: 'FETCH_AUTH_OPTIONS', diff --git a/packages/data-stores/src/auth/test/flows.ts b/packages/data-stores/src/auth/test/flows.ts index c9b136cb8e866..1c358d585161b 100644 --- a/packages/data-stores/src/auth/test/flows.ts +++ b/packages/data-stores/src/auth/test/flows.ts @@ -11,18 +11,19 @@ */ import { dispatch, select } from '@wordpress/data'; import { parse } from 'qs'; +import wpcomRequest from 'wpcom-proxy-request'; /** * Internal dependencies */ import { register } from '..'; -import { wpcomRequest as wpcomRequestOriginal } from '../../utils'; -jest.mock( '../../utils', () => ( { - wpcomRequest: jest.fn(), +jest.mock( 'wpcom-proxy-request', () => ( { + __esModule: true, + default: jest.fn(), + requestAllBlogsAccess: jest.fn( () => Promise.resolve() ), } ) ); -const wpcomRequest: jest.Mock = wpcomRequestOriginal as any; const fetch = jest.fn(); beforeAll( () => { @@ -34,7 +35,7 @@ beforeEach( () => { store = register( { client_id: '', client_secret: '' } ); fetch.mockReset(); - wpcomRequest.mockReset(); + ( wpcomRequest as jest.Mock ).mockReset(); } ); describe( 'password login flow', () => { @@ -44,7 +45,10 @@ describe( 'password login flow', () => { expect( getLoginFlowState() ).toBe( 'ENTER_USERNAME_OR_EMAIL' ); - wpcomRequest.mockResolvedValue( { email_verified: true, passwordless: false } ); + ( wpcomRequest as jest.Mock ).mockResolvedValue( { + email_verified: true, + passwordless: false, + } ); await submitUsernameOrEmail( 'user1' ); diff --git a/packages/data-stores/src/shared-types.ts b/packages/data-stores/src/shared-types.ts new file mode 100644 index 0000000000000..f42c43565f86c --- /dev/null +++ b/packages/data-stores/src/shared-types.ts @@ -0,0 +1,4 @@ +export interface WpcomClientCredentials { + client_id: string; + client_secret: string; +} diff --git a/packages/data-stores/src/site/controls.ts b/packages/data-stores/src/site/controls.ts index 615c712b43fef..963eba87cf7ba 100644 --- a/packages/data-stores/src/site/controls.ts +++ b/packages/data-stores/src/site/controls.ts @@ -1,10 +1,18 @@ +/** + * External dependencies + */ +import wpcomRequest, { requestAllBlogsAccess } from 'wpcom-proxy-request'; + /** * Internal dependencies */ -import { wpcomRequest, WpcomClientCredentials } from '../utils'; import { CreateSiteAction } from './types'; +import { WpcomClientCredentials } from '../shared-types'; export default function createControls( clientCreds: WpcomClientCredentials ) { + requestAllBlogsAccess().catch( () => { + throw new Error( 'Could not get all blog access.' ); + } ); return { CREATE_SITE: async ( action: CreateSiteAction ) => { const { authToken, ...providedParams } = action.params; diff --git a/packages/data-stores/src/site/index.ts b/packages/data-stores/src/site/index.ts index 872ae11b3a8e1..0b6e705e92593 100644 --- a/packages/data-stores/src/site/index.ts +++ b/packages/data-stores/src/site/index.ts @@ -12,7 +12,7 @@ import * as actions from './actions'; import * as selectors from './selectors'; import createControls from './controls'; import { DispatchFromMap, SelectFromMap } from '../mapped-types'; -import { WpcomClientCredentials } from '../utils'; +import { WpcomClientCredentials } from '../shared-types'; export * from './types'; export { State }; diff --git a/packages/data-stores/src/user/controls.ts b/packages/data-stores/src/user/controls.ts index 2d096fc040b45..e63d1720d3f1c 100644 --- a/packages/data-stores/src/user/controls.ts +++ b/packages/data-stores/src/user/controls.ts @@ -1,10 +1,18 @@ +/** + * External dependencies + */ +import wpcomRequest, { requestAllBlogsAccess } from 'wpcom-proxy-request'; + /** * Internal dependencies */ -import { wpcomRequest, WpcomClientCredentials } from '../utils'; import { CreateAccountAction } from './types'; +import { WpcomClientCredentials } from '../shared-types'; export default function createControls( clientCreds: WpcomClientCredentials ) { + requestAllBlogsAccess().catch( () => { + throw new Error( 'Could not get all blog access.' ); + } ); return { CREATE_ACCOUNT: async ( action: CreateAccountAction ) => { const defaultParams = { diff --git a/packages/data-stores/src/user/index.ts b/packages/data-stores/src/user/index.ts index eef475ffcca60..e2804a557d45f 100644 --- a/packages/data-stores/src/user/index.ts +++ b/packages/data-stores/src/user/index.ts @@ -13,7 +13,7 @@ import * as resolvers from './resolvers'; import * as selectors from './selectors'; import createControls from './controls'; import { DispatchFromMap, SelectFromMap } from '../mapped-types'; -import { WpcomClientCredentials } from '../utils'; +import { WpcomClientCredentials } from '../shared-types'; export * from './types'; export { State }; diff --git a/packages/data-stores/src/utils/index.ts b/packages/data-stores/src/utils/index.ts deleted file mode 100644 index 350c0bac17baa..0000000000000 --- a/packages/data-stores/src/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './wpcom-wrapper'; diff --git a/packages/data-stores/src/utils/wpcom-wrapper.ts b/packages/data-stores/src/utils/wpcom-wrapper.ts deleted file mode 100644 index b09f1ebfed692..0000000000000 --- a/packages/data-stores/src/utils/wpcom-wrapper.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * External dependencies - */ -import wpcomProxyRequest, { reloadProxy } from 'wpcom-proxy-request'; -import debugFactory from 'debug'; - -const debug = debugFactory( 'data-stores:utils:wpcom-wrapper' ); - -export interface WpcomClientCredentials { - client_id: string; - client_secret: string; -} - -export interface WpcomRequestParams { - path?: string; - method?: string; - apiVersion?: string; - body?: object; - token?: string; - metaAPI?: { - accessAllUsersBlogs?: boolean; - }; -} - -export function wpcomRequest< T >( params: WpcomRequestParams ): Promise< T > { - return new Promise( ( resolve, reject ) => { - wpcomProxyRequest( params, ( err: Error, res: T ) => { - debug( res ); - err ? reject( err ) : resolve( res ); - } ); - } ); -} -/* - * Reloading the proxy ensures that the proxy iframe has set the correct API cookie. - * This is particularly useful for making authenticated API requests - * *after* the user has logged in or signed up without the need for a hard browser refresh. - */ -export function reloadWpcomProxy(): void { - reloadProxy(); -} - -wpcomProxyRequest( { metaAPI: { accessAllUsersBlogs: true } }, ( error: Error ) => { - if ( error ) { - throw error; - } - debug( 'Proxy now running in "access all user\'s blogs" mode' ); -} ); diff --git a/packages/data-stores/tsconfig.json b/packages/data-stores/tsconfig.json index 0a8be6b76037f..bef7dbde805be 100644 --- a/packages/data-stores/tsconfig.json +++ b/packages/data-stores/tsconfig.json @@ -29,6 +29,6 @@ "incremental": true, "tsBuildInfoFile": "../../.tsc-cache/data-stores" }, - "files": [ "./src/index.ts", "./global.d.ts" ], + "files": [ "./src/index.ts" ], "exclude": [ "**/docs/*", "**/test/*" ] } diff --git a/packages/wpcom-proxy-request/History.md b/packages/wpcom-proxy-request/History.md index 889b3b85ace79..3afd725e75cc6 100644 --- a/packages/wpcom-proxy-request/History.md +++ b/packages/wpcom-proxy-request/History.md @@ -1,5 +1,9 @@ # 6.0.0 / TBD +- Breaking: Return Promise (rather than `XMLHttpRequest` instance) if no callback argument is provided. + - In practice, most people have probably been using the callback rather than the returned `XMLHttpRequest` instance, so this shouldn't be a breaking change for most. +- Add `requestAllBlogsAccess()`. +- Add a few type definitions. - Move the published `build/` folder to `dist/` to align with other Calypso packages - Upgrade dependency 'debug' to 4.1.1 - Remove `component-event` dependency, use `addEventListener`/`removeEventListener` directly diff --git a/packages/wpcom-proxy-request/package.json b/packages/wpcom-proxy-request/package.json index 0795a523782b2..29aa8553c50be 100644 --- a/packages/wpcom-proxy-request/package.json +++ b/packages/wpcom-proxy-request/package.json @@ -30,9 +30,11 @@ }, "files": [ "dist", + "types", "History.md", "README.md" ], + "types": "types", "scripts": { "clean": "npx rimraf dist", "prepublish": "npm run clean", diff --git a/packages/wpcom-proxy-request/src/index.js b/packages/wpcom-proxy-request/src/index.js index f865243b666f7..b6645d4733cfe 100644 --- a/packages/wpcom-proxy-request/src/index.js +++ b/packages/wpcom-proxy-request/src/index.js @@ -101,7 +101,7 @@ debug( 'using "origin": %o', origin ); * @param {Function} [fn] - callback response * @returns {window.XMLHttpRequest} XMLHttpRequest instance */ -const request = ( originalParams, fn ) => { +const makeRequest = ( originalParams, fn ) => { const params = Object.assign( {}, originalParams ); debug( 'request(%o)', params ); @@ -170,6 +170,40 @@ const request = ( originalParams, fn ) => { return xhr; }; +/** + * Performs a "proxied REST API request". This happens by calling + * `iframe.postMessage()` on the proxy iframe instance, which from there + * takes care of WordPress.com user authentication (via the currently + * logged-in user's cookies). + * + * If no function is specified as second parameter, a promise is returned. + * + * @param {object} originalParams - request parameters + * @param {Function} [fn] - callback response + * @returns {window.XMLHttpRequest|Promise} XMLHttpRequest instance or Promise + */ +const request = ( originalParams, fn ) => { + // if callback is provided, behave traditionally + if ( 'function' === typeof fn ) { + // request method + return makeRequest( originalParams, fn ); + } + + // but if not, return a Promise + return new Promise( ( res, rej ) => { + makeRequest( originalParams, ( err, response ) => { + err ? rej( err ) : res( response ); + } ); + } ); +}; + +/** + * Set proxy to "access all users' blogs" mode. + */ +export function requestAllBlogsAccess() { + return request( { metaAPI: { accessAllUsersBlogs: true } } ); +} + /** * Calls the `postMessage()` function on the