@lowdefy/api 4.5.1 → 4.6.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.
Files changed (67) hide show
  1. package/dist/context/createApiContext.js +1 -1
  2. package/dist/context/createAuthorize.js +8 -4
  3. package/dist/context/createEvaluateOperators.js +16 -2
  4. package/dist/context/createReadConfigFile.js +3 -3
  5. package/dist/index.js +4 -3
  6. package/dist/routes/auth/callbacks/addUserFieldsToSession.js +1 -1
  7. package/dist/routes/auth/callbacks/addUserFieldsToToken.js +1 -1
  8. package/dist/routes/auth/callbacks/createCallbackPlugins.js +1 -1
  9. package/dist/routes/auth/callbacks/createCallbacks.js +1 -1
  10. package/dist/routes/auth/callbacks/createJWTCallback.js +1 -1
  11. package/dist/routes/auth/callbacks/createRedirectCallback.js +1 -1
  12. package/dist/routes/auth/callbacks/createSessionCallback.js +1 -1
  13. package/dist/routes/auth/callbacks/createSignInCallback.js +1 -1
  14. package/dist/routes/auth/createAdapter.js +1 -1
  15. package/dist/routes/auth/createLogger.js +21 -17
  16. package/dist/routes/auth/createProviders.js +1 -1
  17. package/dist/routes/auth/events/createCreateUserEvent.js +1 -1
  18. package/dist/routes/auth/events/createEventPlugins.js +1 -1
  19. package/dist/routes/auth/events/createEvents.js +1 -1
  20. package/dist/routes/auth/events/createLinkAccountEvent.js +1 -1
  21. package/dist/routes/auth/events/createSessionEvent.js +1 -1
  22. package/dist/routes/auth/events/createSignInEvent.js +1 -1
  23. package/dist/routes/auth/events/createSignOutEvent.js +1 -1
  24. package/dist/routes/auth/events/createUpdateUserEvent.js +1 -1
  25. package/dist/routes/auth/getNextAuthConfig.js +2 -2
  26. package/dist/routes/endpoints/addStepResult.js +1 -1
  27. package/dist/routes/endpoints/authorizeApiEndpoint.js +3 -3
  28. package/dist/routes/endpoints/callEndpoint.js +1 -1
  29. package/dist/routes/endpoints/control/controlFor.js +11 -7
  30. package/dist/routes/endpoints/control/controlIf.js +4 -4
  31. package/dist/routes/endpoints/control/controlLog.js +11 -6
  32. package/dist/routes/endpoints/control/controlParallel.js +1 -1
  33. package/dist/routes/endpoints/control/controlParallelFor.js +11 -7
  34. package/dist/routes/endpoints/control/controlReject.js +4 -3
  35. package/dist/routes/endpoints/control/controlReturn.js +2 -2
  36. package/dist/routes/endpoints/control/controlSetState.js +1 -1
  37. package/dist/routes/endpoints/control/controlSwitch.js +4 -4
  38. package/dist/routes/endpoints/control/controlThrow.js +4 -3
  39. package/dist/routes/endpoints/control/controlTry.js +1 -1
  40. package/dist/routes/endpoints/control/handleControl.js +1 -1
  41. package/dist/routes/endpoints/getEndpointConfig.js +3 -3
  42. package/dist/routes/endpoints/handleRequest.js +1 -1
  43. package/dist/routes/endpoints/runRoutine.js +2 -2
  44. package/dist/routes/log/formatValidationError.js +31 -0
  45. package/dist/routes/log/logClientError.js +109 -0
  46. package/dist/routes/log/validatePluginSchema.js +25 -0
  47. package/dist/routes/page/getPageConfig.js +6 -5
  48. package/dist/routes/request/authorizeRequest.js +5 -3
  49. package/dist/routes/request/callRequest.js +1 -1
  50. package/dist/routes/request/callRequestResolver.js +43 -6
  51. package/dist/routes/request/checkConnectionRead.js +8 -4
  52. package/dist/routes/request/checkConnectionWrite.js +8 -4
  53. package/dist/routes/request/evaluateOperators.js +1 -1
  54. package/dist/routes/request/getConnection.js +5 -3
  55. package/dist/routes/request/getConnectionConfig.js +8 -4
  56. package/dist/routes/request/getRequestConfig.js +3 -3
  57. package/dist/routes/request/getRequestResolver.js +5 -3
  58. package/dist/routes/request/validateSchemas.js +39 -20
  59. package/dist/routes/rootConfig/getHomeAndMenus.js +1 -1
  60. package/dist/routes/rootConfig/getLowdefyGlobal.js +1 -1
  61. package/dist/routes/rootConfig/getRootConfig.js +1 -1
  62. package/dist/routes/rootConfig/menus/filterMenuList.js +1 -1
  63. package/dist/routes/rootConfig/menus/filterMenus.js +1 -1
  64. package/dist/routes/rootConfig/menus/getMenus.js +1 -1
  65. package/dist/test/testContext.js +4 -1
  66. package/package.json +8 -7
  67. package/dist/context/errors.js +0 -33
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -15,15 +15,16 @@
15
15
  */ async function controlThrow(context, routineContext, { control }) {
16
16
  const { evaluateOperators } = context;
17
17
  const { items } = routineContext;
18
+ const location = control['~k'] ?? ':throw';
18
19
  const message = evaluateOperators({
19
20
  input: control[':throw'],
20
21
  items,
21
- location: 'TODO'
22
+ location
22
23
  });
23
24
  const cause = evaluateOperators({
24
25
  input: control[':cause'],
25
26
  items,
26
- location: 'TODO'
27
+ location
27
28
  });
28
29
  const error = new Error(message, {
29
30
  cause
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,11 +12,11 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { ConfigurationError } from '../../context/errors.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
16
  async function getEndpointConfig({ logger, readConfigFile }, { endpointId }) {
17
17
  const endpoint = await readConfigFile(`api/${endpointId}.json`);
18
18
  if (!endpoint) {
19
- const err = new ConfigurationError(`API Endpoint "${endpointId}" does not exist.`);
19
+ const err = new ConfigError(`API Endpoint "${endpointId}" does not exist.`);
20
20
  logger.debug({
21
21
  params: {
22
22
  endpointId
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -50,7 +50,7 @@ async function runRoutine(context, routineContext, { routine }) {
50
50
  }
51
51
  });
52
52
  } catch (error) {
53
- context.logger.error(error);
53
+ await context.handleError(error);
54
54
  return {
55
55
  status: 'error',
56
56
  error
@@ -0,0 +1,31 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ function formatValidationError({ ajvError, pluginLabel, typeName, fieldLabel }) {
16
+ const path = ajvError.instancePath ? ajvError.instancePath.substring(1).replace(/\//g, '.') : null;
17
+ const fieldName = path || ajvError.params?.missingProperty || 'unknown';
18
+ switch(ajvError.keyword){
19
+ case 'type':
20
+ return `${pluginLabel} "${typeName}" ${fieldLabel} "${fieldName}" must be type "${ajvError.params.type}".`;
21
+ case 'enum':
22
+ return `${pluginLabel} "${typeName}" ${fieldLabel} "${fieldName}" must be one of ${JSON.stringify(ajvError.params.allowedValues)}.`;
23
+ case 'additionalProperties':
24
+ return `${pluginLabel} "${typeName}" ${fieldLabel} "${ajvError.params.additionalProperty}" is not allowed.`;
25
+ case 'required':
26
+ return `${pluginLabel} "${typeName}" required ${fieldLabel} "${ajvError.params.missingProperty}" is missing.`;
27
+ default:
28
+ return `${pluginLabel} "${typeName}" ${fieldLabel} "${fieldName}" ${ajvError.message}.`;
29
+ }
30
+ }
31
+ export default formatValidationError;
@@ -0,0 +1,109 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import { ConfigError, loadAndResolveErrorLocation } from '@lowdefy/errors';
16
+ import { serializer, type } from '@lowdefy/helpers';
17
+ import formatValidationError from './formatValidationError.js';
18
+ import validatePluginSchema from './validatePluginSchema.js';
19
+ const validationConfigs = {
20
+ BlockError: {
21
+ schemaFile: 'plugins/blockSchemas.json',
22
+ schemaKey: 'properties',
23
+ pluginLabel: 'Block',
24
+ fieldLabel: 'property'
25
+ },
26
+ ActionError: {
27
+ schemaFile: 'plugins/actionSchemas.json',
28
+ schemaKey: 'params',
29
+ pluginLabel: 'Action',
30
+ fieldLabel: 'param'
31
+ },
32
+ OperatorError: {
33
+ schemaFile: 'plugins/operatorSchemas.json',
34
+ schemaKey: 'params',
35
+ pluginLabel: 'Operator',
36
+ fieldLabel: 'param'
37
+ }
38
+ };
39
+ async function logClientError(context, serializedError) {
40
+ const { logger } = context;
41
+ const error = serializer.deserialize(serializedError);
42
+ // Schema validation for plugin errors with received data
43
+ const validationConfig = validationConfigs[error.name];
44
+ let validationError = null;
45
+ if (validationConfig && !type.isNone(error.received)) {
46
+ try {
47
+ const schemas = await context.readConfigFile(validationConfig.schemaFile);
48
+ if (schemas) {
49
+ const schema = schemas[error.typeName];
50
+ if (schema) {
51
+ const data = error.name === 'OperatorError' ? Object.values(error.received)[0] : error.received;
52
+ const ajvErrors = validatePluginSchema({
53
+ data,
54
+ schema,
55
+ schemaKey: validationConfig.schemaKey
56
+ });
57
+ if (ajvErrors) {
58
+ const displayName = error.name === 'OperatorError' && error.methodName ? `${error.typeName}.${error.methodName}` : error.typeName;
59
+ const messages = ajvErrors.map((ajvError)=>formatValidationError({
60
+ ajvError,
61
+ pluginLabel: validationConfig.pluginLabel,
62
+ typeName: displayName,
63
+ fieldLabel: validationConfig.fieldLabel
64
+ }));
65
+ const message = messages.length === 1 ? messages[0] : `${validationConfig.pluginLabel} "${displayName}" has invalid ${validationConfig.schemaKey}:\n${messages.map((m)=>` - ${m}`).join('\n')}`;
66
+ validationError = new ConfigError(message, {
67
+ configKey: error.configKey,
68
+ cause: error
69
+ });
70
+ }
71
+ }
72
+ }
73
+ } catch (e) {
74
+ logger.warn(e);
75
+ }
76
+ }
77
+ const location = await loadAndResolveErrorLocation({
78
+ error,
79
+ readConfigFile: context.readConfigFile,
80
+ configDirectory: context.configDirectory
81
+ });
82
+ if (location) {
83
+ error.source = location.source;
84
+ error.config = location.config;
85
+ }
86
+ // If validation produced a ConfigError, log only that (cause chain shows original)
87
+ if (validationError) {
88
+ if (location) {
89
+ validationError.source = location.source;
90
+ validationError.config = location.config;
91
+ }
92
+ logger.error(validationError);
93
+ return {
94
+ success: true,
95
+ source: error.source ?? null,
96
+ config: error.config ?? null,
97
+ error,
98
+ configError: serializer.serialize(validationError)
99
+ };
100
+ }
101
+ logger.error(error);
102
+ return {
103
+ success: true,
104
+ source: error.source ?? null,
105
+ config: error.config ?? null,
106
+ error
107
+ };
108
+ }
109
+ export default logClientError;
@@ -0,0 +1,25 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import { validate } from '@lowdefy/ajv';
16
+ function validatePluginSchema({ data, schema, schemaKey }) {
17
+ if (!schema?.[schemaKey]) return null;
18
+ const { valid, errors } = validate({
19
+ schema: schema[schemaKey],
20
+ data,
21
+ returnErrors: true
22
+ });
23
+ return valid ? null : errors;
24
+ }
25
+ export default validatePluginSchema;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,14 +12,15 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ async function getPageConfig({ authorize, readConfigFile }, { pageId }) {
15
+ */ import { serializer } from '@lowdefy/helpers';
16
+ async function getPageConfig({ authorize, readConfigFile }, { pageId }) {
16
17
  const pageConfig = await readConfigFile(`pages/${pageId}/${pageId}.json`);
17
18
  if (pageConfig && authorize(pageConfig)) {
18
19
  // eslint-disable-next-line no-unused-vars
19
20
  const { auth, ...rest } = pageConfig;
20
- return {
21
- ...rest
22
- };
21
+ // Use serializer.serialize to ensure ~k keys (non-enumerable after deserialize)
22
+ // are made enumerable again for JSON transfer to client
23
+ return serializer.serialize(rest);
23
24
  }
24
25
  return null;
25
26
  }
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,7 +12,7 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { ConfigurationError } from '../../context/errors.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
16
  function authorizeRequest({ authorize, logger }, { requestConfig }) {
17
17
  if (!authorize(requestConfig)) {
18
18
  logger.debug({
@@ -21,7 +21,9 @@ function authorizeRequest({ authorize, logger }, { requestConfig }) {
21
21
  auth_config: requestConfig.auth
22
22
  });
23
23
  // Throw does not exist error to avoid leaking information that request exists to unauthorized users
24
- throw new ConfigurationError(`Request "${requestConfig.requestId}" does not exist.`);
24
+ throw new ConfigError(`Request "${requestConfig.requestId}" does not exist.`, {
25
+ configKey: requestConfig['~k']
26
+ });
25
27
  }
26
28
  logger.debug({
27
29
  event: 'debug_request_authorize',
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,7 +12,7 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { RequestError } from '../../context/errors.js';
15
+ */ import { ConfigError, RequestError, ServiceError } from '@lowdefy/errors';
16
16
  async function callRequestResolver({ blockId, endpointId, logger, pageId, payload }, { connectionProperties, requestConfig, requestProperties, requestResolver }) {
17
17
  try {
18
18
  const response = await requestResolver({
@@ -27,15 +27,52 @@ async function callRequestResolver({ blockId, endpointId, logger, pageId, payloa
27
27
  });
28
28
  return response;
29
29
  } catch (error) {
30
- const err = new RequestError(error.message);
30
+ // Add configKey to any error for location tracing
31
+ if (!error.configKey) {
32
+ error.configKey = requestConfig['~k'];
33
+ }
34
+ if (error instanceof ConfigError) {
35
+ logger.debug({
36
+ params: {
37
+ id: requestConfig.requestId,
38
+ type: requestConfig.type
39
+ },
40
+ err: error
41
+ }, error.message);
42
+ throw error;
43
+ }
44
+ // Check if this is a service error (network, timeout, 5xx)
45
+ if (ServiceError.isServiceError(error)) {
46
+ const serviceError = new ServiceError(undefined, {
47
+ cause: error,
48
+ service: requestConfig.connectionId,
49
+ configKey: requestConfig['~k']
50
+ });
51
+ logger.debug({
52
+ params: {
53
+ id: requestConfig.requestId,
54
+ type: requestConfig.type
55
+ },
56
+ err: serviceError
57
+ }, serviceError.message);
58
+ throw serviceError;
59
+ }
60
+ // Wrap other errors in RequestError (request/connection logic error)
61
+ const requestError = new RequestError(error.message, {
62
+ cause: error,
63
+ typeName: requestConfig.type,
64
+ received: requestProperties,
65
+ location: `${requestConfig.connectionId}/${requestConfig.requestId}`,
66
+ configKey: requestConfig['~k']
67
+ });
31
68
  logger.debug({
32
69
  params: {
33
70
  id: requestConfig.requestId,
34
71
  type: requestConfig.type
35
72
  },
36
- err
37
- }, err.message);
38
- throw err;
73
+ err: requestError
74
+ }, requestError.message);
75
+ throw requestError;
39
76
  }
40
77
  }
41
78
  export default callRequestResolver;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,14 +12,18 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { ConfigurationError } from '../../context/errors.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
16
  function checkConnectionRead({ logger }, { connectionConfig, connectionProperties, requestConfig, requestResolver }) {
17
17
  if (requestResolver.meta.checkRead && connectionProperties.read === false) {
18
- const err = new ConfigurationError(`Connection "${connectionConfig.connectionId}" does not allow reads.`);
18
+ const configKey = requestConfig['~k'];
19
+ const err = new ConfigError(`Connection "${connectionConfig.connectionId}" does not allow reads.`, {
20
+ configKey
21
+ });
19
22
  logger.debug({
20
23
  params: {
21
24
  connectionId: connectionConfig.connectionId,
22
- requestId: requestConfig.requestId
25
+ requestId: requestConfig.requestId,
26
+ configKey
23
27
  },
24
28
  err
25
29
  }, err.message);
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,14 +12,18 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { ConfigurationError } from '../../context/errors.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
16
  function checkConnectionWrite({ logger }, { connectionConfig, connectionProperties, requestConfig, requestResolver }) {
17
17
  if (requestResolver.meta.checkWrite && connectionProperties.write !== true) {
18
- const err = new ConfigurationError(`Connection "${connectionConfig.connectionId}" does not allow writes.`);
18
+ const configKey = requestConfig['~k'];
19
+ const err = new ConfigError(`Connection "${connectionConfig.connectionId}" does not allow writes.`, {
20
+ configKey
21
+ });
19
22
  logger.debug({
20
23
  params: {
21
24
  connectionId: connectionConfig.connectionId,
22
- requestId: requestConfig.requestId
25
+ requestId: requestConfig.requestId,
26
+ configKey
23
27
  },
24
28
  err
25
29
  }, err.message);
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,11 +12,13 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { ConfigurationError } from '../../context/errors.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
16
  function getConnection({ connections, logger }, { connectionConfig }) {
17
17
  const connection = connections[connectionConfig.type];
18
18
  if (!connection) {
19
- const err = new ConfigurationError(`Connection type "${connectionConfig.type}" can not be found.`);
19
+ const err = new ConfigError(`Connection type "${connectionConfig.type}" can not be found.`, {
20
+ configKey: connectionConfig['~k']
21
+ });
20
22
  logger.debug({
21
23
  params: {
22
24
  id: connectionConfig.connectionId,
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,12 +12,14 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { ConfigurationError } from '../../context/errors.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
16
  async function getConnectionConfig({ logger, readConfigFile }, { requestConfig }) {
17
17
  const { connectionId, requestId } = requestConfig;
18
18
  let err;
19
19
  if (!connectionId) {
20
- err = new ConfigurationError(`Request "${requestId}" does not specify a connection.`);
20
+ err = new ConfigError(`Request "${requestId}" does not specify a connection.`, {
21
+ configKey: requestConfig['~k']
22
+ });
21
23
  logger.debug({
22
24
  params: {
23
25
  requestId
@@ -28,7 +30,9 @@ async function getConnectionConfig({ logger, readConfigFile }, { requestConfig }
28
30
  }
29
31
  const connection = await readConfigFile(`connections/${connectionId}.json`);
30
32
  if (!connection) {
31
- err = new ConfigurationError(`Connection "${connectionId}" does not exist.`);
33
+ err = new ConfigError(`Connection "${connectionId}" does not exist.`, {
34
+ configKey: requestConfig['~k']
35
+ });
32
36
  logger.debug({
33
37
  params: {
34
38
  requestId
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,11 +12,11 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { ConfigurationError } from '../../context/errors.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
16
  async function getRequestConfig({ logger, readConfigFile }, { pageId, requestId }) {
17
17
  const request = await readConfigFile(`pages/${pageId}/requests/${requestId}.json`);
18
18
  if (!request) {
19
- const err = new ConfigurationError(`Request "${requestId}" does not exist.`);
19
+ const err = new ConfigError(`Request "${requestId}" does not exist.`);
20
20
  logger.debug({
21
21
  params: {
22
22
  pageId,
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,11 +12,13 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { ConfigurationError } from '../../context/errors.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
16
  function getRequestResolver({ logger }, { connection, requestConfig }) {
17
17
  const requestResolver = connection.requests[requestConfig.type];
18
18
  if (!requestResolver) {
19
- const err = new ConfigurationError(`Request type "${requestConfig.type}" can not be found.`);
19
+ const err = new ConfigError(`Request type "${requestConfig.type}" can not be found.`, {
20
+ configKey: requestConfig['~k']
21
+ });
20
22
  logger.debug({
21
23
  params: {
22
24
  id: requestConfig.requestId,
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -13,27 +13,46 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ import { validate } from '@lowdefy/ajv';
16
- import { ConfigurationError } from '../../context/errors.js';
16
+ import { ConfigError } from '@lowdefy/errors';
17
+ import formatValidationError from '../log/formatValidationError.js';
17
18
  function validateSchemas({ logger }, { connection, connectionProperties, requestConfig, requestResolver, requestProperties }) {
18
- try {
19
- validate({
20
- schema: connection.schema,
21
- data: connectionProperties
22
- });
23
- validate({
24
- schema: requestResolver.schema,
25
- data: requestProperties
19
+ const configKey = requestConfig['~k'];
20
+ const messages = [];
21
+ const connectionResult = validate({
22
+ schema: connection.schema,
23
+ data: connectionProperties,
24
+ returnErrors: true
25
+ });
26
+ if (!connectionResult.valid) {
27
+ for (const ajvError of connectionResult.errors){
28
+ messages.push(formatValidationError({
29
+ ajvError,
30
+ pluginLabel: 'Connection',
31
+ typeName: requestConfig.connectionId,
32
+ fieldLabel: 'property'
33
+ }));
34
+ }
35
+ }
36
+ const requestResult = validate({
37
+ schema: requestResolver.schema,
38
+ data: requestProperties,
39
+ returnErrors: true
40
+ });
41
+ if (!requestResult.valid) {
42
+ for (const ajvError of requestResult.errors){
43
+ messages.push(formatValidationError({
44
+ ajvError,
45
+ pluginLabel: 'Request',
46
+ typeName: requestConfig.type,
47
+ fieldLabel: 'property'
48
+ }));
49
+ }
50
+ }
51
+ if (messages.length > 0) {
52
+ const message = messages.length === 1 ? messages[0] : `Request "${requestConfig.requestId}" has schema validation errors:\n${messages.map((m)=>` - ${m}`).join('\n')}`;
53
+ throw new ConfigError(message, {
54
+ configKey
26
55
  });
27
- } catch (error) {
28
- const err = new ConfigurationError(error.message);
29
- logger.debug({
30
- params: {
31
- id: requestConfig.requestId,
32
- type: requestConfig.type
33
- },
34
- err
35
- }, err.message);
36
- throw err;
37
56
  }
38
57
  }
39
58
  export default validateSchemas;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.