MOBILE-4529 addons: Fix lazy instances overrides

main
Noel De Martin 2024-03-07 15:48:16 +01:00
parent 69cf1ead4c
commit b39dc1b90e
10 changed files with 61 additions and 20 deletions

View File

@ -51,7 +51,14 @@ export function getPrefetchHandlerInstance(): CoreCourseModulePrefetchHandler {
}); });
prefetchHandlerInstance.setEagerInstance(new AddonModSurveyPrefetchHandlerService()); prefetchHandlerInstance.setEagerInstance(new AddonModSurveyPrefetchHandlerService());
prefetchHandlerInstance.setLazyInstanceMethods(['sync']); prefetchHandlerInstance.setLazyMethods(['sync']);
prefetchHandlerInstance.setLazyOverrides([
'prefetch',
'isEnabled',
'invalidateModule',
'invalidateContent',
'getIntroFiles',
]);
} }
return prefetchHandlerInstance; return prefetchHandlerInstance;

View File

@ -39,7 +39,7 @@ export function getCronHandlerInstance(): CoreCronHandler {
}); });
lazyHandler.setEagerInstance(new AddonModSurveySyncCronHandlerService()); lazyHandler.setEagerInstance(new AddonModSurveySyncCronHandlerService());
lazyHandler.setLazyInstanceMethods(['execute', 'getInterval']); lazyHandler.setLazyMethods(['execute', 'getInterval']);
return lazyHandler; return lazyHandler;
} }

View File

@ -51,7 +51,7 @@ export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentS
}); });
lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyAccumulativeHandlerService()); lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyAccumulativeHandlerService());
lazyHandler.setLazyInstanceMethods([ lazyHandler.setLazyMethods([
'getComponent', 'getComponent',
'getOriginalValues', 'getOriginalValues',
'hasDataChanged', 'hasDataChanged',

View File

@ -51,7 +51,7 @@ export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentS
}); });
lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyCommentsHandlerService()); lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyCommentsHandlerService());
lazyHandler.setLazyInstanceMethods([ lazyHandler.setLazyMethods([
'getComponent', 'getComponent',
'getOriginalValues', 'getOriginalValues',
'hasDataChanged', 'hasDataChanged',

View File

@ -51,7 +51,7 @@ export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentS
}); });
lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyNumErrorsHandlerService()); lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyNumErrorsHandlerService());
lazyHandler.setLazyInstanceMethods([ lazyHandler.setLazyMethods([
'getComponent', 'getComponent',
'getOriginalValues', 'getOriginalValues',
'hasDataChanged', 'hasDataChanged',

View File

@ -51,7 +51,7 @@ export function getAssessmentStrategyHandlerInstance(): AddonWorkshopAssessmentS
}); });
lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyRubricHandlerService()); lazyHandler.setEagerInstance(new AddonModWorkshopAssessmentStrategyRubricHandlerService());
lazyHandler.setLazyInstanceMethods([ lazyHandler.setLazyMethods([
'getComponent', 'getComponent',
'getOriginalValues', 'getOriginalValues',
'hasDataChanged', 'hasDataChanged',

View File

@ -48,7 +48,13 @@ export function getPrefetchHandlerInstance(): CoreCourseModulePrefetchHandler {
}); });
lazyHandler.setEagerInstance(new AddonModWorkshopPrefetchHandlerService()); lazyHandler.setEagerInstance(new AddonModWorkshopPrefetchHandlerService());
lazyHandler.setLazyInstanceMethods(['sync']); lazyHandler.setLazyMethods(['sync']);
lazyHandler.setLazyOverrides([
'getFiles',
'invalidateContent',
'isDownloadable',
'prefetch',
]);
return lazyHandler; return lazyHandler;
} }

View File

@ -39,7 +39,7 @@ export function getCronHandlerInstance(): CoreCronHandler {
}); });
lazyHandler.setEagerInstance(new AddonModWorkshopSyncCronHandlerService()); lazyHandler.setEagerInstance(new AddonModWorkshopSyncCronHandlerService());
lazyHandler.setLazyInstanceMethods(['execute', 'getInterval']); lazyHandler.setLazyMethods(['execute', 'getInterval']);
return lazyHandler; return lazyHandler;
} }

View File

@ -28,15 +28,19 @@ function createAsyncInstanceWrapper<
lazyConstructor?: () => TLazyInstance | Promise<TLazyInstance>, lazyConstructor?: () => TLazyInstance | Promise<TLazyInstance>,
): AsyncInstanceWrapper<TLazyInstance, TEagerInstance> { ): AsyncInstanceWrapper<TLazyInstance, TEagerInstance> {
let promisedInstance: CorePromisedValue<TLazyInstance> | null = null; let promisedInstance: CorePromisedValue<TLazyInstance> | null = null;
let lazyInstanceMethods: Array<string | symbol>; let lazyMethods: Array<string | number | symbol> | null = null;
let lazyOverrides: Array<keyof TEagerInstance> | null = null;
let eagerInstance: TEagerInstance; let eagerInstance: TEagerInstance;
return { return {
get instance() { get instance() {
return promisedInstance?.value ?? undefined; return promisedInstance?.value ?? undefined;
}, },
get lazyInstanceMethods() { get lazyMethods() {
return lazyInstanceMethods; return lazyMethods;
},
get lazyOverrides() {
return lazyOverrides;
}, },
get eagerInstance() { get eagerInstance() {
return eagerInstance; return eagerInstance;
@ -68,8 +72,11 @@ function createAsyncInstanceWrapper<
promisedInstance.resolve(instance); promisedInstance.resolve(instance);
}, },
setLazyInstanceMethods(methods) { setLazyMethods(methods) {
lazyInstanceMethods = methods; lazyMethods = methods;
},
setLazyOverrides(overrides) {
lazyOverrides = overrides;
}, },
setEagerInstance(instance) { setEagerInstance(instance) {
eagerInstance = instance; eagerInstance = instance;
@ -116,14 +123,16 @@ export interface AsyncInstanceWrapper<
TEagerInstance extends AsyncObject = Partial<TLazyInstance> TEagerInstance extends AsyncObject = Partial<TLazyInstance>
> { > {
instance?: TLazyInstance; instance?: TLazyInstance;
lazyInstanceMethods?: Array<string | symbol>; lazyMethods?: Array<string | number | symbol> | null;
lazyOverrides?: Array<keyof TEagerInstance> | null;
eagerInstance?: TEagerInstance; eagerInstance?: TEagerInstance;
getInstance(): Promise<TLazyInstance>; getInstance(): Promise<TLazyInstance>;
getProperty<P extends keyof TLazyInstance>(property: P): Promise<TLazyInstance[P]>; getProperty<P extends keyof TLazyInstance>(property: P): Promise<TLazyInstance[P]>;
setInstance(instance: TLazyInstance): void; setInstance(instance: TLazyInstance): void;
setLazyInstanceMethods<const T extends Array<string | symbol>>( setLazyMethods<const T extends Array<string | number | symbol>>(
methods: LazyMethodsGuard<T, TLazyInstance, TEagerInstance>, methods: LazyMethodsGuard<T, TLazyInstance, TEagerInstance>,
): void; ): void;
setLazyOverrides(methods: Array<keyof TEagerInstance>): void;
setEagerInstance(eagerInstance: TEagerInstance): void; setEagerInstance(eagerInstance: TEagerInstance): void;
setLazyConstructor(lazyConstructor: () => TLazyInstance | Promise<TLazyInstance>): void; setLazyConstructor(lazyConstructor: () => TLazyInstance | Promise<TLazyInstance>): void;
resetInstance(): void; resetInstance(): void;
@ -156,7 +165,7 @@ export type AsyncInstance<TLazyInstance extends TEagerInstance, TEagerInstance e
/** /**
* Guard type to make sure that lazy methods match what the lazy class implements. * Guard type to make sure that lazy methods match what the lazy class implements.
*/ */
export type LazyMethodsGuard<TMethods extends Array<string | symbol>, TLazyInstance, TEagerInstance> = export type LazyMethodsGuard<TMethods extends Array<string | number | symbol>, TLazyInstance, TEagerInstance> =
TupleMatches<TMethods, Exclude<keyof TLazyInstance, keyof TEagerInstance>> extends true ? TMethods : never; TupleMatches<TMethods, Exclude<keyof TLazyInstance, keyof TEagerInstance>> extends true ? TMethods : never;
/** /**
@ -172,7 +181,9 @@ export function asyncInstance<TLazyInstance extends TEagerInstance, TEagerInstan
const wrapper = createAsyncInstanceWrapper<TLazyInstance, TEagerInstance>(lazyConstructor); const wrapper = createAsyncInstanceWrapper<TLazyInstance, TEagerInstance>(lazyConstructor);
return new Proxy(wrapper, { return new Proxy(wrapper, {
get: (target, property, receiver) => { get: (target, p, receiver) => {
const property = p as keyof TEagerInstance;
if (property in target) { if (property in target) {
return Reflect.get(target, property, receiver); return Reflect.get(target, property, receiver);
} }
@ -185,11 +196,19 @@ export function asyncInstance<TLazyInstance extends TEagerInstance, TEagerInstan
: value; : value;
} }
if (wrapper.eagerInstance && property in wrapper.eagerInstance) { if (
wrapper.eagerInstance &&
property in wrapper.eagerInstance &&
!wrapper.lazyOverrides?.includes(property)
) {
return Reflect.get(wrapper.eagerInstance, property, receiver); return Reflect.get(wrapper.eagerInstance, property, receiver);
} }
if (wrapper.lazyInstanceMethods && !wrapper.lazyInstanceMethods.includes(property)) { if (
wrapper.lazyMethods &&
!wrapper.lazyMethods.includes(property) &&
!wrapper.lazyOverrides?.includes(property)
) {
return undefined; return undefined;
} }

View File

@ -39,11 +39,20 @@ describe('AsyncInstance', () => {
expect(await asyncService.isEager()).toBe(false); expect(await asyncService.isEager()).toBe(false);
}); });
it('initialize instance for forced eager properties', async () => {
const asyncService = asyncInstance(() => new LazyService());
asyncService.setEagerInstance(new EagerService());
asyncService.setLazyOverrides(['isEager']);
expect(await asyncService.isEager()).toBe(false);
});
it('does not return undefined methods when they are declared', async () => { it('does not return undefined methods when they are declared', async () => {
const asyncService = asyncInstance<LazyService, EagerService>(() => new LazyService()); const asyncService = asyncInstance<LazyService, EagerService>(() => new LazyService());
asyncService.setEagerInstance(new EagerService()); asyncService.setEagerInstance(new EagerService());
asyncService.setLazyInstanceMethods(['hello', 'goodbye']); asyncService.setLazyMethods(['hello', 'goodbye']);
expect(asyncService.hello).not.toBeUndefined(); expect(asyncService.hello).not.toBeUndefined();
expect(asyncService.goodbye).not.toBeUndefined(); expect(asyncService.goodbye).not.toBeUndefined();