Skip to content

Commit 8f3b694

Browse files
authored
feat: Add option to change the log level of the logs emitted by triggers (#8328)
1 parent 0a8670d commit 8f3b694

File tree

10 files changed

+286
-137
lines changed

10 files changed

+286
-137
lines changed

resources/buildConfigDefinitions.js

+129-109
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,33 @@ const nestedOptionTypes = [
2424
'PasswordPolicyOptions',
2525
'SecurityOptions',
2626
'SchemaOptions',
27+
'LogLevels',
2728
];
2829

2930
/** The prefix of environment variables for nested options. */
3031
const nestedOptionEnvPrefix = {
31-
'AccountLockoutOptions': 'PARSE_SERVER_ACCOUNT_LOCKOUT_',
32-
'CustomPagesOptions': 'PARSE_SERVER_CUSTOM_PAGES_',
33-
'DatabaseOptions': 'PARSE_SERVER_DATABASE_',
34-
'FileUploadOptions': 'PARSE_SERVER_FILE_UPLOAD_',
35-
'IdempotencyOptions': 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_',
36-
'LiveQueryOptions': 'PARSE_SERVER_LIVEQUERY_',
37-
'LiveQueryServerOptions': 'PARSE_LIVE_QUERY_SERVER_',
38-
'PagesCustomUrlsOptions': 'PARSE_SERVER_PAGES_CUSTOM_URL_',
39-
'PagesOptions': 'PARSE_SERVER_PAGES_',
40-
'PagesRoute': 'PARSE_SERVER_PAGES_ROUTE_',
41-
'ParseServerOptions': 'PARSE_SERVER_',
42-
'PasswordPolicyOptions': 'PARSE_SERVER_PASSWORD_POLICY_',
43-
'SecurityOptions': 'PARSE_SERVER_SECURITY_',
44-
'SchemaOptions': 'PARSE_SERVER_SCHEMA_',
32+
AccountLockoutOptions: 'PARSE_SERVER_ACCOUNT_LOCKOUT_',
33+
CustomPagesOptions: 'PARSE_SERVER_CUSTOM_PAGES_',
34+
DatabaseOptions: 'PARSE_SERVER_DATABASE_',
35+
FileUploadOptions: 'PARSE_SERVER_FILE_UPLOAD_',
36+
IdempotencyOptions: 'PARSE_SERVER_EXPERIMENTAL_IDEMPOTENCY_',
37+
LiveQueryOptions: 'PARSE_SERVER_LIVEQUERY_',
38+
LiveQueryServerOptions: 'PARSE_LIVE_QUERY_SERVER_',
39+
PagesCustomUrlsOptions: 'PARSE_SERVER_PAGES_CUSTOM_URL_',
40+
PagesOptions: 'PARSE_SERVER_PAGES_',
41+
PagesRoute: 'PARSE_SERVER_PAGES_ROUTE_',
42+
ParseServerOptions: 'PARSE_SERVER_',
43+
PasswordPolicyOptions: 'PARSE_SERVER_PASSWORD_POLICY_',
44+
SecurityOptions: 'PARSE_SERVER_SECURITY_',
45+
SchemaOptions: 'PARSE_SERVER_SCHEMA_',
46+
LogLevels: 'PARSE_SERVER_LOG_LEVELS_',
4547
};
4648

4749
function last(array) {
4850
return array[array.length - 1];
4951
}
5052

51-
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
53+
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
5254
function toENV(key) {
5355
let str = '';
5456
let previousIsUpper = false;
@@ -68,13 +70,15 @@ function toENV(key) {
6870
}
6971

7072
function getCommentValue(comment) {
71-
if (!comment) { return }
73+
if (!comment) {
74+
return;
75+
}
7276
return comment.value.trim();
7377
}
7478

7579
function getENVPrefix(iface) {
7680
if (nestedOptionEnvPrefix[iface.id.name]) {
77-
return nestedOptionEnvPrefix[iface.id.name]
81+
return nestedOptionEnvPrefix[iface.id.name];
7882
}
7983
}
8084

@@ -86,11 +90,11 @@ function processProperty(property, iface) {
8690
if (!firstComment) {
8791
return;
8892
}
89-
const lines = firstComment.split('\n').map((line) => line.trim());
93+
const lines = firstComment.split('\n').map(line => line.trim());
9094
let help = '';
9195
let envLine;
9296
let defaultLine;
93-
lines.forEach((line) => {
97+
lines.forEach(line => {
9498
if (line.indexOf(':ENV:') === 0) {
9599
envLine = line;
96100
} else if (line.indexOf(':DEFAULT:') === 0) {
@@ -103,7 +107,7 @@ function processProperty(property, iface) {
103107
if (envLine) {
104108
env = envLine.split(' ')[1];
105109
} else {
106-
env = (prefix + toENV(name));
110+
env = prefix + toENV(name);
107111
}
108112
let defaultValue;
109113
if (defaultLine) {
@@ -123,21 +127,20 @@ function processProperty(property, iface) {
123127
defaultValue,
124128
types: property.value.types,
125129
typeAnnotation: property.value.typeAnnotation,
126-
required: isRequired
130+
required: isRequired,
127131
};
128132
}
129133

130-
131134
function doInterface(iface) {
132135
return iface.body.properties
133136
.sort((a, b) => a.key.name.localeCompare(b.key.name))
134-
.map((prop) => processProperty(prop, iface))
135-
.filter((e) => e !== undefined);
137+
.map(prop => processProperty(prop, iface))
138+
.filter(e => e !== undefined);
136139
}
137140

138141
function mapperFor(elt, t) {
139142
const p = t.identifier('parsers');
140-
const wrap = (identifier) => t.memberExpression(p, identifier);
143+
const wrap = identifier => t.memberExpression(p, identifier);
141144

142145
if (t.isNumberTypeAnnotation(elt)) {
143146
return t.callExpression(wrap(t.identifier('numberParser')), [t.stringLiteral(elt.name)]);
@@ -171,27 +174,29 @@ function parseDefaultValue(elt, value, t) {
171174
literalValue = t.numericLiteral(parsers.numberOrBoolParser('')(value));
172175
} else if (t.isArrayTypeAnnotation(elt)) {
173176
const array = parsers.objectParser(value);
174-
literalValue = t.arrayExpression(array.map((value) => {
175-
if (typeof value == 'string') {
176-
return t.stringLiteral(value);
177-
} else if (typeof value == 'number') {
178-
return t.numericLiteral(value);
179-
} else if (typeof value == 'object') {
180-
const object = parsers.objectParser(value);
181-
const props = Object.entries(object).map(([k, v]) => {
182-
if (typeof v == 'string') {
183-
return t.objectProperty(t.identifier(k), t.stringLiteral(v));
184-
} else if (typeof v == 'number') {
185-
return t.objectProperty(t.identifier(k), t.numericLiteral(v));
186-
} else if (typeof v == 'boolean') {
187-
return t.objectProperty(t.identifier(k), t.booleanLiteral(v));
188-
}
189-
});
190-
return t.objectExpression(props);
191-
} else {
192-
throw new Error('Unable to parse array');
193-
}
194-
}));
177+
literalValue = t.arrayExpression(
178+
array.map(value => {
179+
if (typeof value == 'string') {
180+
return t.stringLiteral(value);
181+
} else if (typeof value == 'number') {
182+
return t.numericLiteral(value);
183+
} else if (typeof value == 'object') {
184+
const object = parsers.objectParser(value);
185+
const props = Object.entries(object).map(([k, v]) => {
186+
if (typeof v == 'string') {
187+
return t.objectProperty(t.identifier(k), t.stringLiteral(v));
188+
} else if (typeof v == 'number') {
189+
return t.objectProperty(t.identifier(k), t.numericLiteral(v));
190+
} else if (typeof v == 'boolean') {
191+
return t.objectProperty(t.identifier(k), t.booleanLiteral(v));
192+
}
193+
});
194+
return t.objectExpression(props);
195+
} else {
196+
throw new Error('Unable to parse array');
197+
}
198+
})
199+
);
195200
} else if (t.isAnyTypeAnnotation(elt)) {
196201
literalValue = t.arrayExpression([]);
197202
} else if (t.isBooleanTypeAnnotation(elt)) {
@@ -204,15 +209,16 @@ function parseDefaultValue(elt, value, t) {
204209

205210
if (nestedOptionTypes.includes(type)) {
206211
const object = parsers.objectParser(value);
207-
const props = Object.keys(object).map((key) => {
212+
const props = Object.keys(object).map(key => {
208213
return t.objectProperty(key, object[value]);
209214
});
210215
literalValue = t.objectExpression(props);
211216
}
212217
if (type == 'ProtectedFields') {
213218
const prop = t.objectProperty(
214-
t.stringLiteral('_User'), t.objectPattern([
215-
t.objectProperty(t.stringLiteral('*'), t.arrayExpression([t.stringLiteral('email')]))
219+
t.stringLiteral('_User'),
220+
t.objectPattern([
221+
t.objectProperty(t.stringLiteral('*'), t.arrayExpression([t.stringLiteral('email')])),
216222
])
217223
);
218224
literalValue = t.objectExpression([prop]);
@@ -223,62 +229,69 @@ function parseDefaultValue(elt, value, t) {
223229

224230
function inject(t, list) {
225231
let comments = '';
226-
const results = list.map((elt) => {
227-
if (!elt.name) {
228-
return;
229-
}
230-
const props = ['env', 'help'].map((key) => {
231-
if (elt[key]) {
232-
return t.objectProperty(t.stringLiteral(key), t.stringLiteral(elt[key]));
232+
const results = list
233+
.map(elt => {
234+
if (!elt.name) {
235+
return;
233236
}
234-
}).filter((e) => e !== undefined);
235-
if (elt.required) {
236-
props.push(t.objectProperty(t.stringLiteral('required'), t.booleanLiteral(true)))
237-
}
238-
const action = mapperFor(elt, t);
239-
if (action) {
240-
props.push(t.objectProperty(t.stringLiteral('action'), action))
241-
}
242-
if (elt.defaultValue) {
243-
const parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
244-
if (parsedValue) {
245-
props.push(t.objectProperty(t.stringLiteral('default'), parsedValue));
246-
} else {
247-
throw new Error(`Unable to parse value for ${elt.name} `);
237+
const props = ['env', 'help']
238+
.map(key => {
239+
if (elt[key]) {
240+
return t.objectProperty(t.stringLiteral(key), t.stringLiteral(elt[key]));
241+
}
242+
})
243+
.filter(e => e !== undefined);
244+
if (elt.required) {
245+
props.push(t.objectProperty(t.stringLiteral('required'), t.booleanLiteral(true)));
248246
}
249-
}
250-
let type = elt.type.replace('TypeAnnotation', '');
251-
if (type === 'Generic') {
252-
type = elt.typeAnnotation.id.name;
253-
}
254-
if (type === 'Array') {
255-
type = elt.typeAnnotation.elementType.id
256-
? `${elt.typeAnnotation.elementType.id.name}[]`
257-
: `${elt.typeAnnotation.elementType.type.replace('TypeAnnotation', '')}[]`;
258-
}
259-
if (type === 'NumberOrBoolean') {
260-
type = 'Number|Boolean';
261-
}
262-
if (type === 'NumberOrString') {
263-
type = 'Number|String';
264-
}
265-
if (type === 'Adapter') {
266-
const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name;
267-
type = `Adapter<${adapterType}>`;
268-
}
269-
comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`;
270-
const obj = t.objectExpression(props);
271-
return t.objectProperty(t.stringLiteral(elt.name), obj);
272-
}).filter((elt) => {
273-
return elt != undefined;
274-
});
247+
const action = mapperFor(elt, t);
248+
if (action) {
249+
props.push(t.objectProperty(t.stringLiteral('action'), action));
250+
}
251+
if (elt.defaultValue) {
252+
const parsedValue = parseDefaultValue(elt, elt.defaultValue, t);
253+
if (parsedValue) {
254+
props.push(t.objectProperty(t.stringLiteral('default'), parsedValue));
255+
} else {
256+
throw new Error(`Unable to parse value for ${elt.name} `);
257+
}
258+
}
259+
let type = elt.type.replace('TypeAnnotation', '');
260+
if (type === 'Generic') {
261+
type = elt.typeAnnotation.id.name;
262+
}
263+
if (type === 'Array') {
264+
type = elt.typeAnnotation.elementType.id
265+
? `${elt.typeAnnotation.elementType.id.name}[]`
266+
: `${elt.typeAnnotation.elementType.type.replace('TypeAnnotation', '')}[]`;
267+
}
268+
if (type === 'NumberOrBoolean') {
269+
type = 'Number|Boolean';
270+
}
271+
if (type === 'NumberOrString') {
272+
type = 'Number|String';
273+
}
274+
if (type === 'Adapter') {
275+
const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name;
276+
type = `Adapter<${adapterType}>`;
277+
}
278+
comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`;
279+
const obj = t.objectExpression(props);
280+
return t.objectProperty(t.stringLiteral(elt.name), obj);
281+
})
282+
.filter(elt => {
283+
return elt != undefined;
284+
});
275285
return { results, comments };
276286
}
277287

278288
const makeRequire = function (variableName, module, t) {
279-
const decl = t.variableDeclarator(t.identifier(variableName), t.callExpression(t.identifier('require'), [t.stringLiteral(module)]));
280-
return t.variableDeclaration('var', [decl])
281-
}
289+
const decl = t.variableDeclarator(
290+
t.identifier(variableName),
291+
t.callExpression(t.identifier('require'), [t.stringLiteral(module)])
292+
);
293+
return t.variableDeclaration('var', [decl]);
294+
};
282295
let docs = ``;
283296
const plugin = function (babel) {
284297
const t = babel.types;
@@ -294,27 +307,34 @@ const plugin = function (babel) {
294307
},
295308
ExportDeclaration: function (path) {
296309
// Export declaration on an interface
297-
if (path.node && path.node.declaration && path.node.declaration.type == 'InterfaceDeclaration') {
310+
if (
311+
path.node &&
312+
path.node.declaration &&
313+
path.node.declaration.type == 'InterfaceDeclaration'
314+
) {
298315
const { results, comments } = inject(t, doInterface(path.node.declaration));
299316
const id = path.node.declaration.id.name;
300317
const exports = t.memberExpression(moduleExports, t.identifier(id));
301318
docs += `/**\n * @interface ${id}\n${comments} */\n\n`;
302-
path.replaceWith(
303-
t.assignmentExpression('=', exports, t.objectExpression(results))
304-
)
319+
path.replaceWith(t.assignmentExpression('=', exports, t.objectExpression(results)));
305320
}
306-
}
307-
}
308-
}
321+
},
322+
},
323+
};
309324
};
310325

311326
const auxiliaryCommentBefore = `
312327
**** GENERATED CODE ****
313328
This code has been generated by resources/buildConfigDefinitions.js
314329
Do not edit manually, but update Options/index.js
315-
`
330+
`;
316331

317-
const babel = require("@babel/core");
318-
const res = babel.transformFileSync('./src/Options/index.js', { plugins: [plugin, '@babel/transform-flow-strip-types'], babelrc: false, auxiliaryCommentBefore, sourceMaps: false });
332+
const babel = require('@babel/core');
333+
const res = babel.transformFileSync('./src/Options/index.js', {
334+
plugins: [plugin, '@babel/transform-flow-strip-types'],
335+
babelrc: false,
336+
auxiliaryCommentBefore,
337+
sourceMaps: false,
338+
});
319339
require('fs').writeFileSync('./src/Options/Definitions.js', res.code + '\n');
320340
require('fs').writeFileSync('./src/Options/docs.js', docs);

spec/CloudCodeLogger.spec.js

+35
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,41 @@ describe('Cloud Code Logger', () => {
182182
});
183183
});
184184

185+
it('should log cloud function triggers using the custom log level', async () => {
186+
Parse.Cloud.beforeSave('TestClass', () => {});
187+
Parse.Cloud.afterSave('TestClass', () => {});
188+
189+
const execTest = async (logLevel, triggerBeforeSuccess, triggerAfter) => {
190+
await reconfigureServer({
191+
silent: true,
192+
logLevel,
193+
logLevels: {
194+
triggerAfter,
195+
triggerBeforeSuccess,
196+
},
197+
});
198+
199+
spy = spyOn(Config.get('test').loggerController.adapter, 'log').and.callThrough();
200+
const obj = new Parse.Object('TestClass');
201+
await obj.save();
202+
203+
return {
204+
beforeSave: spy.calls
205+
.allArgs()
206+
.find(log => log[1].startsWith('beforeSave triggered for TestClass for user '))?.[0],
207+
afterSave: spy.calls
208+
.allArgs()
209+
.find(log => log[1].startsWith('afterSave triggered for TestClass for user '))?.[0],
210+
};
211+
};
212+
213+
let calls = await execTest('silly', 'silly', 'debug');
214+
expect(calls).toEqual({ beforeSave: 'silly', afterSave: 'debug' });
215+
216+
calls = await execTest('info', 'warn', 'debug');
217+
expect(calls).toEqual({ beforeSave: 'warn', afterSave: undefined });
218+
});
219+
185220
it('should log cloud function failure', done => {
186221
Parse.Cloud.define('aFunction', () => {
187222
throw 'it failed!';

0 commit comments

Comments
 (0)