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.setLazyInstanceMethods(['sync']);
prefetchHandlerInstance.setLazyMethods(['sync']);
prefetchHandlerInstance.setLazyOverrides([
'prefetch',
'isEnabled',
'invalidateModule',
'invalidateContent',
'getIntroFiles',
]);
}
return prefetchHandlerInstance;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,15 +28,19 @@ function createAsyncInstanceWrapper<
lazyConstructor?: () => TLazyInstance | Promise<TLazyInstance>,
): AsyncInstanceWrapper<TLazyInstance, TEagerInstance> {
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;
return {
get instance() {
return promisedInstance?.value ?? undefined;
},
get lazyInstanceMethods() {
return lazyInstanceMethods;
get lazyMethods() {
return lazyMethods;
},
get lazyOverrides() {
return lazyOverrides;
},
get eagerInstance() {
return eagerInstance;
@ -68,8 +72,11 @@ function createAsyncInstanceWrapper<
promisedInstance.resolve(instance);
},
setLazyInstanceMethods(methods) {
lazyInstanceMethods = methods;
setLazyMethods(methods) {
lazyMethods = methods;
},
setLazyOverrides(overrides) {
lazyOverrides = overrides;
},
setEagerInstance(instance) {
eagerInstance = instance;
@ -116,14 +123,16 @@ export interface AsyncInstanceWrapper<
TEagerInstance extends AsyncObject = Partial<TLazyInstance>
> {
instance?: TLazyInstance;
lazyInstanceMethods?: Array<string | symbol>;
lazyMethods?: Array<string | number | symbol> | null;
lazyOverrides?: Array<keyof TEagerInstance> | null;
eagerInstance?: TEagerInstance;
getInstance(): Promise<TLazyInstance>;
getProperty<P extends keyof TLazyInstance>(property: P): Promise<TLazyInstance[P]>;
setInstance(instance: TLazyInstance): void;
setLazyInstanceMethods<const T extends Array<string | symbol>>(
setLazyMethods<const T extends Array<string | number | symbol>>(
methods: LazyMethodsGuard<T, TLazyInstance, TEagerInstance>,
): void;
setLazyOverrides(methods: Array<keyof TEagerInstance>): void;
setEagerInstance(eagerInstance: TEagerInstance): void;
setLazyConstructor(lazyConstructor: () => TLazyInstance | Promise<TLazyInstance>): 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.
*/
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;
/**
@ -172,7 +181,9 @@ export function asyncInstance<TLazyInstance extends TEagerInstance, TEagerInstan
const wrapper = createAsyncInstanceWrapper<TLazyInstance, TEagerInstance>(lazyConstructor);
return new Proxy(wrapper, {
get: (target, property, receiver) => {
get: (target, p, receiver) => {
const property = p as keyof TEagerInstance;
if (property in target) {
return Reflect.get(target, property, receiver);
}
@ -185,11 +196,19 @@ export function asyncInstance<TLazyInstance extends TEagerInstance, TEagerInstan
: 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);
}
if (wrapper.lazyInstanceMethods && !wrapper.lazyInstanceMethods.includes(property)) {
if (
wrapper.lazyMethods &&
!wrapper.lazyMethods.includes(property) &&
!wrapper.lazyOverrides?.includes(property)
) {
return undefined;
}

View File

@ -39,11 +39,20 @@ describe('AsyncInstance', () => {
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 () => {
const asyncService = asyncInstance<LazyService, EagerService>(() => new LazyService());
asyncService.setEagerInstance(new EagerService());
asyncService.setLazyInstanceMethods(['hello', 'goodbye']);
asyncService.setLazyMethods(['hello', 'goodbye']);
expect(asyncService.hello).not.toBeUndefined();
expect(asyncService.goodbye).not.toBeUndefined();