@forestadmin/mcp-server 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,212 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const schema_fetcher_1 = require("./schema-fetcher");
4
- describe('schema-fetcher', () => {
5
- const originalFetch = global.fetch;
6
- const originalEnv = process.env.FOREST_ENV_SECRET;
7
- let mockFetch;
8
- beforeEach(() => {
9
- mockFetch = jest.fn();
10
- global.fetch = mockFetch;
11
- process.env.FOREST_ENV_SECRET = 'test-env-secret';
12
- (0, schema_fetcher_1.clearSchemaCache)();
13
- });
14
- afterAll(() => {
15
- global.fetch = originalFetch;
16
- process.env.FOREST_ENV_SECRET = originalEnv;
17
- });
18
- // Helper to create JSON:API formatted schema response
19
- const createJsonApiSchema = (collections) => ({
20
- data: collections.map((col, index) => ({
21
- id: `collection-${index}`,
22
- type: 'collections',
23
- attributes: {
24
- name: col.name,
25
- fields: col.fields,
26
- },
27
- })),
28
- meta: {
29
- liana: 'forest-express-sequelize',
30
- liana_version: '9.0.0',
31
- liana_features: null,
32
- },
33
- });
34
- describe('fetchForestSchema', () => {
35
- const mockJsonApiResponse = createJsonApiSchema([
36
- { name: 'users', fields: [{ field: 'id', type: 'Number' }] },
37
- { name: 'products', fields: [{ field: 'name', type: 'String' }] },
38
- ]);
39
- it('should fetch schema from forest server and deserialize JSON:API response', async () => {
40
- mockFetch.mockResolvedValue({
41
- ok: true,
42
- json: () => Promise.resolve(mockJsonApiResponse),
43
- });
44
- const result = await (0, schema_fetcher_1.fetchForestSchema)('https://api.forestadmin.com');
45
- expect(mockFetch).toHaveBeenCalledWith('https://api.forestadmin.com/liana/forest-schema', {
46
- method: 'GET',
47
- headers: {
48
- 'forest-secret-key': 'test-env-secret',
49
- 'Content-Type': 'application/json',
50
- },
51
- });
52
- expect(result.collections).toHaveLength(2);
53
- expect(result.collections[0].name).toBe('users');
54
- expect(result.collections[1].name).toBe('products');
55
- });
56
- it('should use cached schema on subsequent calls', async () => {
57
- mockFetch.mockResolvedValue({
58
- ok: true,
59
- json: () => Promise.resolve(mockJsonApiResponse),
60
- });
61
- const result1 = await (0, schema_fetcher_1.fetchForestSchema)('https://api.forestadmin.com');
62
- const result2 = await (0, schema_fetcher_1.fetchForestSchema)('https://api.forestadmin.com');
63
- expect(mockFetch).toHaveBeenCalledTimes(1);
64
- expect(result1).toEqual(result2);
65
- });
66
- it('should refetch schema after cache expires (24 hours)', async () => {
67
- const oldSchema = {
68
- collections: [{ name: 'old_collection', fields: [] }],
69
- };
70
- const newJsonApiResponse = createJsonApiSchema([{ name: 'new_collection', fields: [] }]);
71
- // Set cache with an old timestamp (more than 24 hours ago)
72
- const oneDayAgo = Date.now() - 25 * 60 * 60 * 1000;
73
- (0, schema_fetcher_1.setSchemaCache)(oldSchema, oneDayAgo);
74
- mockFetch.mockResolvedValue({
75
- ok: true,
76
- json: () => Promise.resolve(newJsonApiResponse),
77
- });
78
- const result = await (0, schema_fetcher_1.fetchForestSchema)('https://api.forestadmin.com');
79
- expect(mockFetch).toHaveBeenCalledTimes(1);
80
- expect(result.collections).toHaveLength(1);
81
- expect(result.collections[0].name).toBe('new_collection');
82
- });
83
- it('should not refetch schema before cache expires', async () => {
84
- const cachedSchema = {
85
- collections: [{ name: 'cached_collection', fields: [] }],
86
- };
87
- // Set cache with a recent timestamp (less than 24 hours ago)
88
- const recentTime = Date.now() - 1 * 60 * 60 * 1000; // 1 hour ago
89
- (0, schema_fetcher_1.setSchemaCache)(cachedSchema, recentTime);
90
- const result = await (0, schema_fetcher_1.fetchForestSchema)('https://api.forestadmin.com');
91
- expect(mockFetch).not.toHaveBeenCalled();
92
- expect(result).toEqual(cachedSchema);
93
- });
94
- it('should throw error when FOREST_ENV_SECRET is not set', async () => {
95
- delete process.env.FOREST_ENV_SECRET;
96
- await expect((0, schema_fetcher_1.fetchForestSchema)('https://api.forestadmin.com')).rejects.toThrow('FOREST_ENV_SECRET environment variable is not set');
97
- });
98
- it('should throw error when response is not ok', async () => {
99
- mockFetch.mockResolvedValue({
100
- ok: false,
101
- text: () => Promise.resolve('Server error message'),
102
- });
103
- await expect((0, schema_fetcher_1.fetchForestSchema)('https://api.forestadmin.com')).rejects.toThrow('Failed to fetch forest schema: Server error message');
104
- });
105
- it('should use custom forest server URL', async () => {
106
- mockFetch.mockResolvedValue({
107
- ok: true,
108
- json: () => Promise.resolve(mockJsonApiResponse),
109
- });
110
- await (0, schema_fetcher_1.fetchForestSchema)('https://custom.forestadmin.com');
111
- expect(mockFetch).toHaveBeenCalledWith('https://custom.forestadmin.com/liana/forest-schema', expect.any(Object));
112
- });
113
- });
114
- describe('getCollectionNames', () => {
115
- it('should extract collection names from schema', () => {
116
- const schema = {
117
- collections: [
118
- { name: 'users', fields: [] },
119
- { name: 'products', fields: [] },
120
- { name: 'orders', fields: [] },
121
- ],
122
- };
123
- const result = (0, schema_fetcher_1.getCollectionNames)(schema);
124
- expect(result).toEqual(['users', 'products', 'orders']);
125
- });
126
- it('should return empty array for empty collections', () => {
127
- const schema = {
128
- collections: [],
129
- };
130
- const result = (0, schema_fetcher_1.getCollectionNames)(schema);
131
- expect(result).toEqual([]);
132
- });
133
- });
134
- describe('clearSchemaCache', () => {
135
- // Helper to create JSON:API formatted schema response
136
- const createJsonApiSchema = (collections) => ({
137
- data: collections.map((col, index) => ({
138
- id: `collection-${index}`,
139
- type: 'collections',
140
- attributes: {
141
- name: col.name,
142
- fields: col.fields,
143
- },
144
- })),
145
- meta: {
146
- liana: 'forest-express-sequelize',
147
- liana_version: '9.0.0',
148
- liana_features: null,
149
- },
150
- });
151
- it('should clear the cache so next fetch makes API call', async () => {
152
- const jsonApiResponse = createJsonApiSchema([{ name: 'test', fields: [] }]);
153
- mockFetch.mockResolvedValue({
154
- ok: true,
155
- json: () => Promise.resolve(jsonApiResponse),
156
- });
157
- // First fetch
158
- await (0, schema_fetcher_1.fetchForestSchema)('https://api.forestadmin.com');
159
- expect(mockFetch).toHaveBeenCalledTimes(1);
160
- // Clear cache
161
- (0, schema_fetcher_1.clearSchemaCache)();
162
- // Second fetch should make API call
163
- await (0, schema_fetcher_1.fetchForestSchema)('https://api.forestadmin.com');
164
- expect(mockFetch).toHaveBeenCalledTimes(2);
165
- });
166
- });
167
- describe('setSchemaCache', () => {
168
- // Helper to create JSON:API formatted schema response
169
- const createJsonApiSchema = (collections) => ({
170
- data: collections.map((col, index) => ({
171
- id: `collection-${index}`,
172
- type: 'collections',
173
- attributes: {
174
- name: col.name,
175
- fields: col.fields,
176
- },
177
- })),
178
- meta: {
179
- liana: 'forest-express-sequelize',
180
- liana_version: '9.0.0',
181
- liana_features: null,
182
- },
183
- });
184
- it('should set cache with current timestamp by default', async () => {
185
- const schema = {
186
- collections: [{ name: 'cached', fields: [] }],
187
- };
188
- (0, schema_fetcher_1.setSchemaCache)(schema);
189
- const result = await (0, schema_fetcher_1.fetchForestSchema)('https://api.forestadmin.com');
190
- expect(mockFetch).not.toHaveBeenCalled();
191
- expect(result).toEqual(schema);
192
- });
193
- it('should set cache with custom timestamp', async () => {
194
- const schema = {
195
- collections: [{ name: 'old_cached', fields: [] }],
196
- };
197
- const newJsonApiResponse = createJsonApiSchema([{ name: 'new', fields: [] }]);
198
- // Set cache with old timestamp
199
- const oldTime = Date.now() - 25 * 60 * 60 * 1000;
200
- (0, schema_fetcher_1.setSchemaCache)(schema, oldTime);
201
- mockFetch.mockResolvedValue({
202
- ok: true,
203
- json: () => Promise.resolve(newJsonApiResponse),
204
- });
205
- const result = await (0, schema_fetcher_1.fetchForestSchema)('https://api.forestadmin.com');
206
- expect(mockFetch).toHaveBeenCalledTimes(1);
207
- expect(result.collections).toHaveLength(1);
208
- expect(result.collections[0].name).toBe('new');
209
- });
210
- });
211
- });
212
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLWZldGNoZXIudGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9zY2hlbWEtZmV0Y2hlci50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEscURBTTBCO0FBRTFCLFFBQVEsQ0FBQyxnQkFBZ0IsRUFBRSxHQUFHLEVBQUU7SUFDOUIsTUFBTSxhQUFhLEdBQUcsTUFBTSxDQUFDLEtBQUssQ0FBQztJQUNuQyxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFDO0lBQ2xELElBQUksU0FBb0IsQ0FBQztJQUV6QixVQUFVLENBQUMsR0FBRyxFQUFFO1FBQ2QsU0FBUyxHQUFHLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztRQUN0QixNQUFNLENBQUMsS0FBSyxHQUFHLFNBQVMsQ0FBQztRQUN6QixPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixHQUFHLGlCQUFpQixDQUFDO1FBQ2xELElBQUEsaUNBQWdCLEdBQUUsQ0FBQztJQUNyQixDQUFDLENBQUMsQ0FBQztJQUVILFFBQVEsQ0FBQyxHQUFHLEVBQUU7UUFDWixNQUFNLENBQUMsS0FBSyxHQUFHLGFBQWEsQ0FBQztRQUM3QixPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixHQUFHLFdBQVcsQ0FBQztJQUM5QyxDQUFDLENBQUMsQ0FBQztJQUVILHNEQUFzRDtJQUN0RCxNQUFNLG1CQUFtQixHQUFHLENBQzFCLFdBQW9GLEVBQ3BGLEVBQUUsQ0FBQyxDQUFDO1FBQ0osSUFBSSxFQUFFLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLEVBQUUsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQ3JDLEVBQUUsRUFBRSxjQUFjLEtBQUssRUFBRTtZQUN6QixJQUFJLEVBQUUsYUFBYTtZQUNuQixVQUFVLEVBQUU7Z0JBQ1YsSUFBSSxFQUFFLEdBQUcsQ0FBQyxJQUFJO2dCQUNkLE1BQU0sRUFBRSxHQUFHLENB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