From 72507661a76fd71e84df1bd00ecdf51591aedb16 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 6 Mar 2025 12:59:52 -0600 Subject: [PATCH 1/5] feat: Add `useMaintenanceKey` to query options --- src/Parse.ts | 19 +++++++++++++++- src/ParseObject.ts | 3 +++ src/ParseQuery.ts | 1 + src/RESTController.ts | 7 +++++- src/__tests__/Parse-test.js | 7 +++++- src/__tests__/ParseQuery-test.js | 15 ++++++++++++ src/__tests__/RESTController-test.js | 22 ++++++++++++++++++ types/Parse.d.ts | 34 +++++++++++----------------- types/ParseQuery.d.ts | 1 + types/RESTController.d.ts | 2 ++ 10 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/Parse.ts b/src/Parse.ts index 0041b3d1e..1e571377f 100644 --- a/src/Parse.ts +++ b/src/Parse.ts @@ -109,9 +109,15 @@ const Parse = { Parse._initialize(applicationId, javaScriptKey); }, - _initialize(applicationId: string, javaScriptKey: string, masterKey?: string) { + _initialize( + applicationId: string, + javaScriptKey: string, + masterKey?: string, + maintenanceKey?: string + ) { CoreManager.set('APPLICATION_ID', applicationId); CoreManager.set('JAVASCRIPT_KEY', javaScriptKey); + CoreManager.set('MAINTENANCE_KEY', maintenanceKey); CoreManager.set('MASTER_KEY', masterKey); CoreManager.set('USE_MASTER_KEY', false); CoreManager.setIfNeeded('EventEmitter', EventEmitter); @@ -197,6 +203,17 @@ const Parse = { return CoreManager.get('MASTER_KEY'); }, + /** + * @member {string} Parse.maintenanceKey + * @static + */ + set maintenanceKey(value) { + CoreManager.set('MAINTENANCE_KEY', value); + }, + get maintenanceKey() { + return CoreManager.get('MAINTENANCE_KEY'); + }, + /** * @member {string} Parse.serverURL * @static diff --git a/src/ParseObject.ts b/src/ParseObject.ts index 208e503c7..2c13992c7 100644 --- a/src/ParseObject.ts +++ b/src/ParseObject.ts @@ -468,6 +468,9 @@ class ParseObject { if (hasOwn(options, 'useMasterKey')) { requestOptions.useMasterKey = !!options.useMasterKey; } + if (hasOwn(options, 'useMaintenanceKey')) { + requestOptions.useMaintenanceKey = !!options.useMaintenanceKey; + } if (hasOwn(options, 'sessionToken') && typeof options.sessionToken === 'string') { requestOptions.sessionToken = options.sessionToken; } diff --git a/src/ParseQuery.ts b/src/ParseQuery.ts index 648060b9b..ae368e5c8 100644 --- a/src/ParseQuery.ts +++ b/src/ParseQuery.ts @@ -13,6 +13,7 @@ import type { RequestOptions, FullOptions } from './RESTController'; type BatchOptions = FullOptions & { batchSize?: number; useMasterKey?: boolean; + useMaintenanceKey?: boolean; sessionToken?: string; context?: { [key: string]: any }; json?: boolean; diff --git a/src/RESTController.ts b/src/RESTController.ts index c85d58e65..994ffcdbb 100644 --- a/src/RESTController.ts +++ b/src/RESTController.ts @@ -7,6 +7,7 @@ import XhrWeapp from './Xhr.weapp'; export type RequestOptions = { useMasterKey?: boolean; + useMaintenanceKey?: boolean; sessionToken?: string; installationId?: string; returnStatus?: boolean; @@ -23,6 +24,7 @@ export type FullOptions = { success?: any; error?: any; useMasterKey?: boolean; + useMaintenanceKey?: boolean; sessionToken?: string; installationId?: string; progress?: any; @@ -36,6 +38,7 @@ type PayloadType = { _JavaScriptKey?: string; _ClientVersion: string; _MasterKey?: string; + _MaintenanceKey?: string; _RevocableSession?: string; _InstallationId?: string; _SessionToken?: string; @@ -273,7 +276,9 @@ const RESTController = { throw new Error('Cannot use the Master Key, it has not been provided.'); } } - + if (options.useMaintenanceKey) { + payload._MaintenanceKey = CoreManager.get('MAINTENANCE_KEY'); + } if (CoreManager.get('FORCE_REVOCABLE_SESSION')) { payload._RevocableSession = '1'; } diff --git a/src/__tests__/Parse-test.js b/src/__tests__/Parse-test.js index e68ea8253..8a4d9c4ea 100644 --- a/src/__tests__/Parse-test.js +++ b/src/__tests__/Parse-test.js @@ -24,10 +24,11 @@ describe('Parse module', () => { expect(CoreManager.get('APPLICATION_ID')).toBe('A'); expect(CoreManager.get('JAVASCRIPT_KEY')).toBe('B'); - Parse._initialize('A', 'B', 'C'); + Parse._initialize('A', 'B', 'C', 'D'); expect(CoreManager.get('APPLICATION_ID')).toBe('A'); expect(CoreManager.get('JAVASCRIPT_KEY')).toBe('B'); expect(CoreManager.get('MASTER_KEY')).toBe('C'); + expect(CoreManager.get('MAINTENANCE_KEY')).toBe('D'); }); it('enables master key use in the node build', () => { @@ -55,6 +56,10 @@ describe('Parse module', () => { expect(CoreManager.get('MASTER_KEY')).toBe('789'); expect(Parse.masterKey).toBe('789'); + Parse.maintenanceKey = '000'; + expect(CoreManager.get('MAINTENANCE_KEY')).toBe('000'); + expect(Parse.maintenanceKey).toBe('000'); + Parse.serverURL = 'http://example.com'; expect(CoreManager.get('SERVER_URL')).toBe('http://example.com'); expect(Parse.serverURL).toBe('http://example.com'); diff --git a/src/__tests__/ParseQuery-test.js b/src/__tests__/ParseQuery-test.js index 22413dc80..ca3a7d57d 100644 --- a/src/__tests__/ParseQuery-test.js +++ b/src/__tests__/ParseQuery-test.js @@ -1263,6 +1263,7 @@ describe('ParseQuery', () => { }, }); expect(options.useMasterKey).toEqual(true); + expect(options.useMaintenanceKey).toEqual(true); expect(options.sessionToken).toEqual('1234'); return Promise.resolve({ results: [], @@ -1274,6 +1275,7 @@ describe('ParseQuery', () => { q.equalTo('size', 'small') .first({ useMasterKey: true, + useMaintenanceKey: true, sessionToken: '1234', }) .then(obj => { @@ -1440,6 +1442,7 @@ describe('ParseQuery', () => { }, }); expect(options.useMasterKey).toEqual(true); + expect(options.useMaintenanceKey).toEqual(true); expect(options.sessionToken).toEqual('1234'); expect(options.context).toEqual(context); return Promise.resolve({ @@ -1451,6 +1454,7 @@ describe('ParseQuery', () => { const q = new ParseQuery('Item'); q.get('I27', { useMasterKey: true, + useMaintenanceKey: true, sessionToken: '1234', context: context, }).then(() => { @@ -1587,6 +1591,7 @@ describe('ParseQuery', () => { }, }); expect(options.useMasterKey).toEqual(true); + expect(options.useMaintenanceKey).toEqual(true); expect(options.sessionToken).toEqual('1234'); expect(options.context).toEqual(context); return Promise.resolve({ @@ -1599,6 +1604,7 @@ describe('ParseQuery', () => { q.containedIn('size', ['small', 'medium']) .find({ useMasterKey: true, + useMaintenanceKey: true, sessionToken: '1234', context: context, }) @@ -1713,6 +1719,7 @@ describe('ParseQuery', () => { it('passes options through to the REST API', async () => { const batchOptions = { useMasterKey: true, + useMaintenanceKey: true, sessionToken: '1234', batchSize: 50, }; @@ -1727,6 +1734,7 @@ describe('ParseQuery', () => { where: {}, }); expect(options.useMasterKey).toBe(true); + expect(options.useMaintenanceKey).toEqual(true); expect(options.sessionToken).toEqual('1234'); }); @@ -1840,6 +1848,7 @@ describe('ParseQuery', () => { it('passes options through to the REST API', async () => { const batchOptions = { useMasterKey: true, + useMaintenanceKey: true, sessionToken: '1234', batchSize: 50, json: true, @@ -1855,6 +1864,7 @@ describe('ParseQuery', () => { where: {}, }); expect(options.useMasterKey).toBe(true); + expect(options.useMaintenanceKey).toEqual(true); expect(options.sessionToken).toEqual('1234'); expect(options.json).toEqual(true); }); @@ -1962,6 +1972,7 @@ describe('ParseQuery', () => { }, }); expect(options.useMasterKey).toEqual(true); + expect(options.useMaintenanceKey).toEqual(true); expect(options.sessionToken).toEqual('1234'); expect(options.context).toEqual(context); return Promise.resolve({ @@ -1986,6 +1997,7 @@ describe('ParseQuery', () => { }, { useMasterKey: true, + useMaintenanceKey: true, sessionToken: '1234', context: context, } @@ -2014,6 +2026,7 @@ describe('ParseQuery', () => { hint: '_id_', }); expect(options.useMasterKey).toEqual(true); + expect(options.useMaintenanceKey).toEqual(true); expect(options.sessionToken).toEqual('1234'); expect(options.context).toEqual(context); return Promise.resolve({ @@ -2039,6 +2052,7 @@ describe('ParseQuery', () => { }, { useMasterKey: true, + useMaintenanceKey: true, sessionToken: '1234', context: context, } @@ -2761,6 +2775,7 @@ describe('ParseQuery', () => { group: { objectId: '$name' }, }); expect(options.useMasterKey).toEqual(true); + expect(options.useMaintenanceKey).toEqual(true); expect(options.requestTask).toBeDefined(); return Promise.resolve({ results: [], diff --git a/src/__tests__/RESTController-test.js b/src/__tests__/RESTController-test.js index 412d5f2e8..45b99ae73 100644 --- a/src/__tests__/RESTController-test.js +++ b/src/__tests__/RESTController-test.js @@ -558,6 +558,28 @@ describe('RESTController', () => { }); }); + it('sends the maintenance key when requested', async () => { + CoreManager.set('MAINTENANCE_KEY', 'MK'); + const xhr = { + setRequestHeader: jest.fn(), + open: jest.fn(), + send: jest.fn(), + }; + RESTController._setXHR(function () { + return xhr; + }); + RESTController.request('GET', 'classes/MyObject', {}, { useMaintenanceKey: true }); + await flushPromises(); + expect(JSON.parse(xhr.send.mock.calls[0][0])).toEqual({ + _method: 'GET', + _ApplicationId: 'A', + _JavaScriptKey: 'B', + _MaintenanceKey: 'MK', + _ClientVersion: 'V', + _InstallationId: 'iid', + }); + }); + it('includes the status code when requested', done => { RESTController._setXHR(mockXHR([{ status: 200, response: { success: true } }])); RESTController.request('POST', 'users', {}, { returnStatus: true }).then(response => { diff --git a/types/Parse.d.ts b/types/Parse.d.ts index 4500ea0ab..a52808c7e 100644 --- a/types/Parse.d.ts +++ b/types/Parse.d.ts @@ -347,16 +347,7 @@ declare const Parse: { headers?: any, options?: import('./RESTController').FullOptions ) => Promise; - handleError: ( - err? /** - * Call this method to set your LocalDatastoreStorage engine - * If using React-Native use {@link Parse.setAsyncStorage Parse.setAsyncStorage()} - * - * @param {LocalDatastoreController} controller a data storage. - * @static - */ - : any - ) => void; + handleError: (err?: any) => void; }): void; getRESTController(): { request: ( @@ -372,16 +363,7 @@ declare const Parse: { headers?: any, options?: import('./RESTController').FullOptions ) => Promise; - handleError: ( - err? /** - * Call this method to set your LocalDatastoreStorage engine - * If using React-Native use {@link Parse.setAsyncStorage Parse.setAsyncStorage()} - * - * @param {LocalDatastoreController} controller a data storage. - * @static - */ - : any - ) => void; + handleError: (err?: any) => void; }; setSchemaController(controller: { purge: (className: string) => Promise; @@ -812,7 +794,12 @@ declare const Parse: { * @static */ initialize(applicationId: string, javaScriptKey: string): void; - _initialize(applicationId: string, javaScriptKey: string, masterKey?: string): void; + _initialize( + applicationId: string, + javaScriptKey: string, + masterKey?: string, + maintenanceKey?: string + ): void; /** * Call this method to set your AsyncStorage engine * Starting Parse@1.11, the ParseSDK do not provide a React AsyncStorage as the ReactNative module @@ -852,6 +839,11 @@ declare const Parse: { * @static */ masterKey: any; + /** + * @member {string} Parse.maintenanceKey + * @static + */ + maintenanceKey: any; /** * @member {string} Parse.serverURL * @static diff --git a/types/ParseQuery.d.ts b/types/ParseQuery.d.ts index 968f13f80..178f34a86 100644 --- a/types/ParseQuery.d.ts +++ b/types/ParseQuery.d.ts @@ -5,6 +5,7 @@ import type { FullOptions } from './RESTController'; type BatchOptions = FullOptions & { batchSize?: number; useMasterKey?: boolean; + useMaintenanceKey?: boolean; sessionToken?: string; context?: { [key: string]: any; diff --git a/types/RESTController.d.ts b/types/RESTController.d.ts index ad7416781..d216b45d3 100644 --- a/types/RESTController.d.ts +++ b/types/RESTController.d.ts @@ -1,5 +1,6 @@ export type RequestOptions = { useMasterKey?: boolean; + useMaintenanceKey?: boolean; sessionToken?: string; installationId?: string; returnStatus?: boolean; @@ -15,6 +16,7 @@ export type FullOptions = { success?: any; error?: any; useMasterKey?: boolean; + useMaintenanceKey?: boolean; sessionToken?: string; installationId?: string; progress?: any; From b6d9ca4dca53657404e513876d17301316ac7339 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 6 Mar 2025 13:12:32 -0600 Subject: [PATCH 2/5] build types --- src/CoreManager.ts | 1 + types/Parse.d.ts | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/CoreManager.ts b/src/CoreManager.ts index 4f9ccea13..1ea209970 100644 --- a/src/CoreManager.ts +++ b/src/CoreManager.ts @@ -320,6 +320,7 @@ const config: Config & { [key: string]: any } = { VERSION: 'js' + require('../package.json').version, APPLICATION_ID: null, JAVASCRIPT_KEY: null, + MAINTENANCE_KEY: null, MASTER_KEY: null, USE_MASTER_KEY: false, PERFORM_USER_REWRITE: true, diff --git a/types/Parse.d.ts b/types/Parse.d.ts index a52808c7e..492e0555f 100644 --- a/types/Parse.d.ts +++ b/types/Parse.d.ts @@ -500,7 +500,17 @@ declare const Parse: { setAsyncStorage(storage: { getItem: ( key: string, - callback?: (error?: Error | null, result?: string | null) => void + callback?: ( + error? /** + * Enable pinning in your application. + * This must be called after `Parse.initialize` in your application. + * + * @param [polling] Allow pinging the server /health endpoint. Default true + * @param [ms] Milliseconds to ping the server. Default 2000ms + * @static + */ : Error | null, + result?: string | null + ) => void ) => Promise; setItem: ( key: string, @@ -540,7 +550,17 @@ declare const Parse: { getAsyncStorage(): { getItem: ( key: string, - callback?: (error?: Error | null, result?: string | null) => void + callback?: ( + error? /** + * Enable pinning in your application. + * This must be called after `Parse.initialize` in your application. + * + * @param [polling] Allow pinging the server /health endpoint. Default true + * @param [ms] Milliseconds to ping the server. Default 2000ms + * @static + */ : Error | null, + result?: string | null + ) => void ) => Promise; setItem: ( key: string, From 59476f53820e143786d761263c7a473657447287 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Sat, 8 Mar 2025 00:43:44 -0600 Subject: [PATCH 3/5] build types --- types/Parse.d.ts | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/types/Parse.d.ts b/types/Parse.d.ts index 633e05c58..1f47e76fe 100644 --- a/types/Parse.d.ts +++ b/types/Parse.d.ts @@ -490,17 +490,7 @@ declare const Parse: { setAsyncStorage(storage: { getItem: ( key: string, - callback?: ( - error? /** - * Enable pinning in your application. - * This must be called after `Parse.initialize` in your application. - * - * @param [polling] Allow pinging the server /health endpoint. Default true - * @param [ms] Milliseconds to ping the server. Default 2000ms - * @static - */ : Error | null, - result?: string | null - ) => void + callback?: (error?: Error | null, result?: string | null) => void ) => Promise; setItem: ( key: string, @@ -540,17 +530,7 @@ declare const Parse: { getAsyncStorage(): { getItem: ( key: string, - callback?: ( - error? /** - * Enable pinning in your application. - * This must be called after `Parse.initialize` in your application. - * - * @param [polling] Allow pinging the server /health endpoint. Default true - * @param [ms] Milliseconds to ping the server. Default 2000ms - * @static - */ : Error | null, - result?: string | null - ) => void + callback?: (error?: Error | null, result?: string | null) => void ) => Promise; setItem: ( key: string, From 1bcd2e7f8a4d432b1532555c5aa552659e8dccff Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Sat, 8 Mar 2025 18:49:13 -0600 Subject: [PATCH 4/5] build types --- types/Parse.d.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/types/Parse.d.ts b/types/Parse.d.ts index 70df28ae4..18965915e 100644 --- a/types/Parse.d.ts +++ b/types/Parse.d.ts @@ -355,7 +355,10 @@ declare const Parse: { create: ( className: string, params: any, - options?: import('./RESTController').RequestOptions + options? /** + * @member {string} Parse.maintenanceKey + * @static + */ : import('./RESTController').RequestOptions ) => Promise; update: ( className: string, @@ -379,7 +382,10 @@ declare const Parse: { create: ( className: string, params: any, - options?: import('./RESTController').RequestOptions + options? /** + * @member {string} Parse.maintenanceKey + * @static + */ : import('./RESTController').RequestOptions ) => Promise; update: ( className: string, From 2b08ec61da478adc6f031a1fc4998cb92704b8cc Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Sun, 9 Mar 2025 11:11:07 -0500 Subject: [PATCH 5/5] fix conflicts --- types/Parse.d.ts | 17 ++++++++++++++--- types/ParseQuery.d.ts | 1 + types/RESTController.d.ts | 2 ++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/types/Parse.d.ts b/types/Parse.d.ts index b36c30a7d..cefbc562f 100644 --- a/types/Parse.d.ts +++ b/types/Parse.d.ts @@ -223,7 +223,10 @@ declare const Parse: { purge: (className: string) => Promise; get: (className: string, options?: import("./RESTController").RequestOptions) => Promise; delete: (className: string, options?: import("./RESTController").RequestOptions) => Promise; - create: (className: string, params: any, options?: import("./RESTController").RequestOptions) => Promise; + create: (className: string, params: any, options? /** + * @member {string} Parse.maintenanceKey + * @static + */: import("./RESTController").RequestOptions) => Promise; update: (className: string, params: any, options?: import("./RESTController").RequestOptions) => Promise; send(className: string, method: string, params: any, options?: import("./RESTController").RequestOptions): Promise; }): void; @@ -231,7 +234,10 @@ declare const Parse: { purge: (className: string) => Promise; get: (className: string, options?: import("./RESTController").RequestOptions) => Promise; delete: (className: string, options?: import("./RESTController").RequestOptions) => Promise; - create: (className: string, params: any, options?: import("./RESTController").RequestOptions) => Promise; + create: (className: string, params: any, options? /** + * @member {string} Parse.maintenanceKey + * @static + */: import("./RESTController").RequestOptions) => Promise; update: (className: string, params: any, options?: import("./RESTController").RequestOptions) => Promise; send(className: string, method: string, params: any, options?: import("./RESTController").RequestOptions): Promise; }; @@ -487,7 +493,7 @@ declare const Parse: { * @static */ initialize(applicationId: string, javaScriptKey: string): void; - _initialize(applicationId: string, javaScriptKey: string, masterKey?: string): void; + _initialize(applicationId: string, javaScriptKey: string, masterKey?: string, maintenanceKey?: string): void; /** * Call this method to set your AsyncStorage engine * Starting Parse@1.11, the ParseSDK do not provide a React AsyncStorage as the ReactNative module @@ -527,6 +533,11 @@ declare const Parse: { * @static */ masterKey: any; + /** + * @member {string} Parse.maintenanceKey + * @static + */ + maintenanceKey: any; /** * @member {string} Parse.serverURL * @static diff --git a/types/ParseQuery.d.ts b/types/ParseQuery.d.ts index e7b1cbaf1..6035c5167 100644 --- a/types/ParseQuery.d.ts +++ b/types/ParseQuery.d.ts @@ -6,6 +6,7 @@ import type { Pointer } from './ParseObject'; type BatchOptions = FullOptions & { batchSize?: number; useMasterKey?: boolean; + useMaintenanceKey?: boolean; sessionToken?: string; context?: { [key: string]: any; diff --git a/types/RESTController.d.ts b/types/RESTController.d.ts index 366c963a0..47863f391 100644 --- a/types/RESTController.d.ts +++ b/types/RESTController.d.ts @@ -1,5 +1,6 @@ export type RequestOptions = { useMasterKey?: boolean; + useMaintenanceKey?: boolean; sessionToken?: string; installationId?: string; returnStatus?: boolean; @@ -15,6 +16,7 @@ export type FullOptions = { success?: any; error?: any; useMasterKey?: boolean; + useMaintenanceKey?: boolean; sessionToken?: string; installationId?: string; progress?: any;