• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alovajs / alova / #219

01 Nov 2024 02:50PM UTC coverage: 95.359% (+1.5%) from 93.83%
#219

push

github

web-flow
Merge pull request #577 from alovajs/changeset-release/main

ci: release

1698 of 1787 branches covered (95.02%)

Branch coverage included in aggregate %.

5801 of 6077 relevant lines covered (95.46%)

223.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.48
/packages/alova/src/functions/manipulateCache.ts
1
import { usingL1CacheAdapters, usingL2CacheAdapters } from '@/alova';
2✔
2
import { globalConfigMap } from '@/globalConfig';
3
import {
4
  clearWithCacheAdapter,
5
  getWithCacheAdapter,
6
  hitTargetCacheWithCacheAdapter,
7
  removeWithCacheAdapter,
8
  setWithCacheAdapter
9
} from '@/storage/cacheWrapper';
10
import {
11
  MEMORY,
12
  PromiseCls,
13
  STORAGE_RESTORE,
14
  getConfig,
15
  getContext,
16
  getLocalCacheConfigParam,
17
  getMethodInternalKey,
18
  getTime,
19
  isArray,
20
  isFn,
21
  len,
22
  mapItem,
23
  undefinedValue
24
} from '@alova/shared';
25
import { AlovaGenerics, CacheController, CacheQueryOptions, CacheSetOptions, Method } from '~/typings';
26

27
/*
28
 * The matchers in the following three functions are Method instance matchers, which are divided into three situations:
29
 * 1. If the matcher is a Method instance, clear the cache of the Method instance.
30
 * 2. If matcher is a string or regular expression, clear the cache of all Method instances that meet the conditions.
31
 * 3. If no matcher is passed in, all caches will be cleared.
32
 */
33

34
/**
35
 * Query cache
36
 * @param matcher Method instance matcher
37
 * @returns Cache data, return undefined if not found
38
 */
39
export const queryCache = async <Responded>(
2✔
40
  matcher: Method<AlovaGenerics<Responded>>,
98✔
41
  { policy = 'all' }: CacheQueryOptions = {}
98✔
42
) => {
98✔
43
  // if key exists, that means it's a method instance.
44
  if (matcher && matcher.key) {
98✔
45
    const { id, l1Cache, l2Cache } = getContext(matcher);
98✔
46
    const methodKey = getMethodInternalKey(matcher);
98✔
47
    const { f: cacheFor, c: controlled, s: store, e: expireMilliseconds, t: tag } = getLocalCacheConfigParam(matcher);
98✔
48
    // if it's controlled cache, it will return the result of cacheFor function.
49
    if (controlled) {
98✔
50
      return (cacheFor as CacheController<Responded>)();
1✔
51
    }
1✔
52

53
    let cachedData: Responded | undefined =
97✔
54
      policy !== 'l2' ? await getWithCacheAdapter(id, methodKey, l1Cache) : undefinedValue;
98✔
55
    if (policy === 'l2') {
98✔
56
      cachedData = await getWithCacheAdapter(id, methodKey, l2Cache, tag);
10✔
57
    } else if (policy === 'all' && !cachedData) {
98✔
58
      if (store && expireMilliseconds(STORAGE_RESTORE) > getTime()) {
29✔
59
        cachedData = await getWithCacheAdapter(id, methodKey, l2Cache, tag);
2✔
60
      }
2✔
61
    }
29✔
62
    return cachedData;
97✔
63
  }
97✔
64
};
98✔
65

66
/**
67
 * Manually set cache response data. If the corresponding methodInstance sets persistent storage, the cache in the persistent storage will also be checked out.
68
 * @param matcher Method instance matcher cache data
69
 */
70
export const setCache = async <Responded>(
2✔
71
  matcher: Method<AlovaGenerics<Responded>> | Method<AlovaGenerics<Responded>>[],
8✔
72
  dataOrUpdater: Responded | ((oldCache?: Responded) => Responded | undefined | void),
8✔
73
  { policy = 'all' }: CacheSetOptions = {}
8✔
74
) => {
8✔
75
  const methodInstances = isArray(matcher) ? matcher : [matcher];
8✔
76
  const batchPromises = methodInstances.map(async methodInstance => {
8✔
77
    const { hitSource } = methodInstance;
10✔
78
    const { id, l1Cache, l2Cache } = getContext(methodInstance);
10✔
79
    const methodKey = getMethodInternalKey(methodInstance);
10✔
80
    const { e: expireMilliseconds, s: toStore, t: tag, c: controlled } = getLocalCacheConfigParam(methodInstance);
10✔
81
    // don't set cache when it's controlled cache.
82
    if (controlled) {
10✔
83
      return;
1✔
84
    }
1✔
85
    let data: any = dataOrUpdater;
9✔
86
    if (isFn(dataOrUpdater)) {
10✔
87
      let cachedData = policy !== 'l2' ? await getWithCacheAdapter(id, methodKey, l1Cache) : undefinedValue;
5!
88
      if (
5✔
89
        policy === 'l2' ||
5✔
90
        (policy === 'all' && !cachedData && toStore && expireMilliseconds(STORAGE_RESTORE) > getTime())
5!
91
      ) {
5!
92
        cachedData = await getWithCacheAdapter(id, methodKey, l2Cache, tag);
×
93
      }
×
94
      data = dataOrUpdater(cachedData);
5✔
95
      if (data === undefinedValue) {
5✔
96
        return;
2✔
97
      }
2✔
98
    }
5✔
99
    return PromiseCls.all([
7✔
100
      policy !== 'l2' && setWithCacheAdapter(id, methodKey, data, expireMilliseconds(MEMORY), l1Cache, hitSource),
10✔
101
      policy === 'l2' || (policy === 'all' && toStore)
10✔
102
        ? setWithCacheAdapter(id, methodKey, data, expireMilliseconds(STORAGE_RESTORE), l2Cache, hitSource, tag)
2✔
103
        : undefinedValue
5✔
104
    ]);
10✔
105
  });
8✔
106
  return PromiseCls.all(batchPromises);
8✔
107
};
8✔
108

109
/**
110
 * invalid cache
111
 * @param matcher Method instance matcher
112
 */
113
export const invalidateCache = async (matcher?: Method | Method[]) => {
2✔
114
  if (!matcher) {
26✔
115
    await PromiseCls.all([clearWithCacheAdapter(usingL1CacheAdapters), clearWithCacheAdapter(usingL2CacheAdapters)]);
22✔
116
    return;
22✔
117
  }
22✔
118
  const methodInstances = isArray(matcher) ? matcher : [matcher];
26✔
119
  const batchPromises = methodInstances.map(methodInstance => {
26✔
120
    const { id, l1Cache, l2Cache } = getContext(methodInstance);
7✔
121
    const { c: controlled } = getLocalCacheConfigParam(methodInstance);
7✔
122
    // don't invalidate cache when it's controlled cache.
123
    if (controlled) {
7✔
124
      return;
1✔
125
    }
1✔
126
    const methodKey = getMethodInternalKey(methodInstance);
6✔
127
    return PromiseCls.all([
6✔
128
      removeWithCacheAdapter(id, methodKey, l1Cache),
6✔
129
      removeWithCacheAdapter(id, methodKey, l2Cache)
6✔
130
    ]);
6✔
131
  });
26✔
132
  await PromiseCls.all(batchPromises);
26✔
133
};
4✔
134

135
/**
136
 * hit(invalidate) target caches by source method
137
 * this is the implementation of auto invalidate cache
138
 * @param sourceMethod source method instance
139
 */
140
export const hitCacheBySource = async <AG extends AlovaGenerics>(sourceMethod: Method<AG>) => {
2✔
141
  // Find the hit target cache and invalidate its cache
142
  // Control the automatic cache invalidation range through global configuration `autoHitCache`
143
  const { autoHitCache } = globalConfigMap;
158✔
144
  const { l1Cache, l2Cache } = getContext(sourceMethod);
158✔
145
  const sourceKey = getMethodInternalKey(sourceMethod);
158✔
146
  const { name: sourceName } = getConfig(sourceMethod);
158✔
147
  const cacheAdaptersInvolved = {
158✔
148
    global: [...usingL1CacheAdapters, ...usingL2CacheAdapters],
158✔
149
    self: [l1Cache, l2Cache],
158✔
150
    close: []
158✔
151
  }[autoHitCache];
158✔
152
  if (cacheAdaptersInvolved && len(cacheAdaptersInvolved)) {
158✔
153
    await PromiseCls.all(
154✔
154
      mapItem(cacheAdaptersInvolved, involvedCacheAdapter =>
154✔
155
        hitTargetCacheWithCacheAdapter(sourceKey, sourceName, involvedCacheAdapter)
1,784✔
156
      )
154✔
157
    );
154✔
158
  }
138✔
159
};
158✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc