@statezero/core 0.2.22 → 0.2.25
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.
- package/dist/actions/backend1/django_app/calculate-hash.d.ts +57 -0
- package/dist/actions/backend1/django_app/calculate-hash.js +80 -0
- package/dist/actions/backend1/django_app/calculate-hash.schema.json +148 -0
- package/dist/actions/backend1/django_app/get-current-username.d.ts +29 -0
- package/dist/actions/backend1/django_app/get-current-username.js +65 -0
- package/dist/actions/backend1/django_app/get-current-username.schema.json +47 -0
- package/dist/actions/backend1/django_app/get-server-status.d.ts +38 -0
- package/dist/actions/backend1/django_app/get-server-status.js +68 -0
- package/dist/actions/backend1/django_app/get-server-status.schema.json +93 -0
- package/dist/actions/backend1/django_app/get-user-info.d.ts +44 -0
- package/dist/actions/backend1/django_app/get-user-info.js +70 -0
- package/dist/actions/backend1/django_app/get-user-info.schema.json +127 -0
- package/dist/actions/backend1/django_app/index.d.ts +1 -0
- package/dist/actions/backend1/django_app/index.js +6 -0
- package/dist/actions/backend1/django_app/process-data.d.ts +51 -0
- package/dist/actions/backend1/django_app/process-data.js +78 -0
- package/dist/actions/backend1/django_app/process-data.schema.json +117 -0
- package/dist/actions/backend1/django_app/send-notification.d.ts +55 -0
- package/dist/actions/backend1/django_app/send-notification.js +81 -0
- package/dist/actions/backend1/django_app/send-notification.schema.json +175 -0
- package/dist/actions/backend1/index.d.ts +1 -0
- package/dist/actions/backend1/index.js +1 -0
- package/dist/actions/default/django_app/calculate-hash.d.ts +57 -0
- package/dist/actions/default/django_app/calculate-hash.js +80 -0
- package/dist/actions/default/django_app/calculate-hash.schema.json +148 -0
- package/dist/actions/default/django_app/get-current-username.d.ts +29 -0
- package/dist/actions/default/django_app/get-current-username.js +65 -0
- package/dist/actions/default/django_app/get-current-username.schema.json +47 -0
- package/dist/actions/default/django_app/get-server-status.d.ts +38 -0
- package/dist/actions/default/django_app/get-server-status.js +68 -0
- package/dist/actions/default/django_app/get-server-status.schema.json +93 -0
- package/dist/actions/default/django_app/get-user-info.d.ts +44 -0
- package/dist/actions/default/django_app/get-user-info.js +70 -0
- package/dist/actions/default/django_app/get-user-info.schema.json +127 -0
- package/dist/actions/default/django_app/index.d.ts +1 -0
- package/dist/actions/default/django_app/index.js +6 -0
- package/dist/actions/default/django_app/process-data.d.ts +51 -0
- package/dist/actions/default/django_app/process-data.js +78 -0
- package/dist/actions/default/django_app/process-data.schema.json +117 -0
- package/dist/actions/default/django_app/send-notification.d.ts +55 -0
- package/dist/actions/default/django_app/send-notification.js +81 -0
- package/dist/actions/default/django_app/send-notification.schema.json +175 -0
- package/dist/actions/default/index.d.ts +1 -0
- package/dist/actions/default/index.js +1 -0
- package/dist/actions/index.d.ts +1 -0
- package/dist/actions/index.js +5 -0
- package/dist/filtering/localFiltering.d.ts +1 -8
- package/dist/filtering/localFiltering.js +123 -47
- package/dist/flavours/django/dates.js +4 -1
- package/dist/flavours/django/serializers.js +8 -4
- package/dist/models/backend1/django_app/comprehensivemodel.d.ts +894 -0
- package/dist/models/backend1/django_app/comprehensivemodel.js +71 -0
- package/dist/models/backend1/django_app/comprehensivemodel.schema.json +870 -0
- package/dist/models/backend1/django_app/custompkmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/custompkmodel.js +69 -0
- package/dist/models/backend1/django_app/custompkmodel.schema.json +71 -0
- package/dist/models/backend1/django_app/dailyrate.d.ts +230 -0
- package/dist/models/backend1/django_app/dailyrate.js +71 -0
- package/dist/models/backend1/django_app/dailyrate.schema.json +212 -0
- package/dist/models/backend1/django_app/deepmodellevel1.d.ts +140 -0
- package/dist/models/backend1/django_app/deepmodellevel1.js +72 -0
- package/dist/models/backend1/django_app/deepmodellevel1.schema.json +114 -0
- package/dist/models/backend1/django_app/deepmodellevel2.d.ts +118 -0
- package/dist/models/backend1/django_app/deepmodellevel2.js +71 -0
- package/dist/models/backend1/django_app/deepmodellevel2.schema.json +92 -0
- package/dist/models/backend1/django_app/deepmodellevel3.d.ts +92 -0
- package/dist/models/backend1/django_app/deepmodellevel3.js +69 -0
- package/dist/models/backend1/django_app/deepmodellevel3.schema.json +69 -0
- package/dist/models/backend1/django_app/dummymodel.d.ts +134 -0
- package/dist/models/backend1/django_app/dummymodel.js +71 -0
- package/dist/models/backend1/django_app/dummymodel.schema.json +109 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.js +69 -0
- package/dist/models/backend1/django_app/dummyrelatedmodel.schema.json +69 -0
- package/dist/models/backend1/django_app/filetest.d.ts +140 -0
- package/dist/models/backend1/django_app/filetest.js +69 -0
- package/dist/models/backend1/django_app/filetest.schema.json +111 -0
- package/dist/models/backend1/django_app/index.d.ts +1 -0
- package/dist/models/backend1/django_app/index.js +21 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.d.ts +118 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel1.schema.json +94 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.d.ts +118 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel2.schema.json +94 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.d.ts +134 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.js +71 -0
- package/dist/models/backend1/django_app/m2mdepthtestlevel3.schema.json +112 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.d.ts +118 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.js +71 -0
- package/dist/models/backend1/django_app/modelwithcustompkrelation.schema.json +93 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.d.ts +134 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.js +71 -0
- package/dist/models/backend1/django_app/modelwithrestrictedfields.schema.json +111 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.d.ts +92 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.js +69 -0
- package/dist/models/backend1/django_app/namefiltercustompkmodel.schema.json +71 -0
- package/dist/models/backend1/django_app/order.d.ts +220 -0
- package/dist/models/backend1/django_app/order.js +71 -0
- package/dist/models/backend1/django_app/order.schema.json +203 -0
- package/dist/models/backend1/django_app/orderitem.d.ts +172 -0
- package/dist/models/backend1/django_app/orderitem.js +72 -0
- package/dist/models/backend1/django_app/orderitem.schema.json +149 -0
- package/dist/models/backend1/django_app/product.d.ts +254 -0
- package/dist/models/backend1/django_app/product.js +71 -0
- package/dist/models/backend1/django_app/product.schema.json +277 -0
- package/dist/models/backend1/django_app/productcategory.d.ts +92 -0
- package/dist/models/backend1/django_app/productcategory.js +69 -0
- package/dist/models/backend1/django_app/productcategory.schema.json +70 -0
- package/dist/models/backend1/django_app/rateplan.d.ts +92 -0
- package/dist/models/backend1/django_app/rateplan.js +69 -0
- package/dist/models/backend1/django_app/rateplan.schema.json +70 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.d.ts +108 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.js +69 -0
- package/dist/models/backend1/django_app/restrictedfieldrelatedmodel.schema.json +87 -0
- package/dist/models/backend1/fileobject.d.ts +4 -0
- package/dist/models/backend1/fileobject.js +9 -0
- package/dist/models/backend1/index.d.ts +2 -0
- package/dist/models/backend1/index.js +2 -0
- package/dist/models/default/django_app/comprehensivemodel.d.ts +894 -0
- package/dist/models/default/django_app/comprehensivemodel.js +71 -0
- package/dist/models/default/django_app/comprehensivemodel.schema.json +870 -0
- package/dist/models/default/django_app/custompkmodel.d.ts +92 -0
- package/dist/models/default/django_app/custompkmodel.js +69 -0
- package/dist/models/default/django_app/custompkmodel.schema.json +71 -0
- package/dist/models/default/django_app/dailyrate.d.ts +230 -0
- package/dist/models/default/django_app/dailyrate.js +71 -0
- package/dist/models/default/django_app/dailyrate.schema.json +212 -0
- package/dist/models/default/django_app/deepmodellevel1.d.ts +128 -0
- package/dist/models/default/django_app/deepmodellevel1.js +72 -0
- package/dist/models/default/django_app/deepmodellevel1.schema.json +102 -0
- package/dist/models/default/django_app/deepmodellevel2.d.ts +106 -0
- package/dist/models/default/django_app/deepmodellevel2.js +71 -0
- package/dist/models/default/django_app/deepmodellevel2.schema.json +80 -0
- package/dist/models/default/django_app/deepmodellevel3.d.ts +80 -0
- package/dist/models/default/django_app/deepmodellevel3.js +69 -0
- package/dist/models/default/django_app/deepmodellevel3.schema.json +57 -0
- package/dist/models/default/django_app/dummymodel.d.ts +122 -0
- package/dist/models/default/django_app/dummymodel.js +71 -0
- package/dist/models/default/django_app/dummymodel.schema.json +97 -0
- package/dist/models/default/django_app/dummyrelatedmodel.d.ts +80 -0
- package/dist/models/default/django_app/dummyrelatedmodel.js +69 -0
- package/dist/models/default/django_app/dummyrelatedmodel.schema.json +57 -0
- package/dist/models/default/django_app/filetest.d.ts +128 -0
- package/dist/models/default/django_app/filetest.js +69 -0
- package/dist/models/default/django_app/filetest.schema.json +99 -0
- package/dist/models/default/django_app/index.d.ts +1 -0
- package/dist/models/default/django_app/index.js +21 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.d.ts +118 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel1.schema.json +94 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.d.ts +118 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel2.schema.json +94 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.d.ts +134 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.js +71 -0
- package/dist/models/default/django_app/m2mdepthtestlevel3.schema.json +112 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.d.ts +118 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.js +71 -0
- package/dist/models/default/django_app/modelwithcustompkrelation.schema.json +93 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.d.ts +134 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.js +71 -0
- package/dist/models/default/django_app/modelwithrestrictedfields.schema.json +111 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.d.ts +92 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.js +69 -0
- package/dist/models/default/django_app/namefiltercustompkmodel.schema.json +71 -0
- package/dist/models/default/django_app/order.d.ts +220 -0
- package/dist/models/default/django_app/order.js +71 -0
- package/dist/models/default/django_app/order.schema.json +203 -0
- package/dist/models/default/django_app/orderitem.d.ts +172 -0
- package/dist/models/default/django_app/orderitem.js +72 -0
- package/dist/models/default/django_app/orderitem.schema.json +149 -0
- package/dist/models/default/django_app/product.d.ts +254 -0
- package/dist/models/default/django_app/product.js +71 -0
- package/dist/models/default/django_app/product.schema.json +277 -0
- package/dist/models/default/django_app/productcategory.d.ts +92 -0
- package/dist/models/default/django_app/productcategory.js +69 -0
- package/dist/models/default/django_app/productcategory.schema.json +70 -0
- package/dist/models/default/django_app/rateplan.d.ts +92 -0
- package/dist/models/default/django_app/rateplan.js +69 -0
- package/dist/models/default/django_app/rateplan.schema.json +70 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.d.ts +108 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.js +69 -0
- package/dist/models/default/django_app/restrictedfieldrelatedmodel.schema.json +87 -0
- package/dist/models/default/fileobject.d.ts +4 -0
- package/dist/models/default/fileobject.js +9 -0
- package/dist/models/default/index.d.ts +2 -0
- package/dist/models/default/index.js +2 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Send notifications to multiple recipients
|
|
3
|
+
*
|
|
4
|
+
* @param string message - Notification message to send
|
|
5
|
+
* @param any[] recipients - List of email addresses to notify
|
|
6
|
+
* @param 'low' | 'high' priority - Notification priority level
|
|
7
|
+
* @param {Object} [axiosOverrides] - Allows overriding Axios request parameters.
|
|
8
|
+
* @returns {Promise<Object>} A promise that resolves with the action's result.
|
|
9
|
+
*/
|
|
10
|
+
export function sendNotification(message: any, recipients: any, priority?: string, axiosOverrides?: Object): Promise<Object>;
|
|
11
|
+
export namespace sendNotification {
|
|
12
|
+
let actionName: string;
|
|
13
|
+
let title: string;
|
|
14
|
+
let app: string;
|
|
15
|
+
let permissions: string[];
|
|
16
|
+
let configKey: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Zod schema for the input of sendNotification.
|
|
20
|
+
* NOTE: This is an object schema for validating the data payload.
|
|
21
|
+
*/
|
|
22
|
+
export const sendNotificationInputSchema: z.ZodObject<{
|
|
23
|
+
message: z.ZodString;
|
|
24
|
+
recipients: z.ZodArray<z.ZodAny, "many">;
|
|
25
|
+
priority: z.ZodDefault<z.ZodOptional<z.ZodEnum<["low", "high"]>>>;
|
|
26
|
+
}, "strip", z.ZodTypeAny, {
|
|
27
|
+
message: string;
|
|
28
|
+
priority: "low" | "high";
|
|
29
|
+
recipients: any[];
|
|
30
|
+
}, {
|
|
31
|
+
message: string;
|
|
32
|
+
recipients: any[];
|
|
33
|
+
priority?: "low" | "high" | undefined;
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Zod schema for the response of sendNotification.
|
|
37
|
+
*/
|
|
38
|
+
export const sendNotificationResponseSchema: z.ZodObject<{
|
|
39
|
+
success: z.ZodBoolean;
|
|
40
|
+
message_id: z.ZodString;
|
|
41
|
+
sent_to: z.ZodNumber;
|
|
42
|
+
queued_at: z.ZodString;
|
|
43
|
+
}, "strip", z.ZodTypeAny, {
|
|
44
|
+
success: boolean;
|
|
45
|
+
message_id: string;
|
|
46
|
+
sent_to: number;
|
|
47
|
+
queued_at: string;
|
|
48
|
+
}, {
|
|
49
|
+
success: boolean;
|
|
50
|
+
message_id: string;
|
|
51
|
+
sent_to: number;
|
|
52
|
+
queued_at: string;
|
|
53
|
+
}>;
|
|
54
|
+
export default sendNotification;
|
|
55
|
+
import { z } from 'zod';
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was auto-generated. Do not make direct changes to the file.
|
|
3
|
+
* Action: Send Notification
|
|
4
|
+
* App: django_app
|
|
5
|
+
*/
|
|
6
|
+
import axios from 'axios';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { configInstance, parseStateZeroError, serializeActionPayload } from '../../../../src';
|
|
9
|
+
import actionSchema from './send-notification.schema.json' assert { type: 'json' };
|
|
10
|
+
/**
|
|
11
|
+
* Zod schema for the input of sendNotification.
|
|
12
|
+
* NOTE: This is an object schema for validating the data payload.
|
|
13
|
+
*/
|
|
14
|
+
export const sendNotificationInputSchema = z.object({ message: z.string().max(500),
|
|
15
|
+
recipients: z.array(z.any()),
|
|
16
|
+
priority: z.enum(["low", "high"]).optional().default("low") });
|
|
17
|
+
/**
|
|
18
|
+
* Zod schema for the response of sendNotification.
|
|
19
|
+
*/
|
|
20
|
+
export const sendNotificationResponseSchema = z.object({ success: z.boolean(),
|
|
21
|
+
message_id: z.string(),
|
|
22
|
+
sent_to: z.number().int(),
|
|
23
|
+
queued_at: z.string().datetime({ message: "Invalid ISO 8601 datetime string" }) });
|
|
24
|
+
/**
|
|
25
|
+
* Send notifications to multiple recipients
|
|
26
|
+
*
|
|
27
|
+
* @param string message - Notification message to send
|
|
28
|
+
* @param any[] recipients - List of email addresses to notify
|
|
29
|
+
* @param 'low' | 'high' priority - Notification priority level
|
|
30
|
+
* @param {Object} [axiosOverrides] - Allows overriding Axios request parameters.
|
|
31
|
+
* @returns {Promise<Object>} A promise that resolves with the action's result.
|
|
32
|
+
*/
|
|
33
|
+
export async function sendNotification(message, recipients, priority = "low", axiosOverrides = {}) {
|
|
34
|
+
// Construct the data payload from the function arguments
|
|
35
|
+
const rawPayload = {
|
|
36
|
+
message,
|
|
37
|
+
recipients,
|
|
38
|
+
priority
|
|
39
|
+
};
|
|
40
|
+
// Serialize payload - handles model instances (extracts PK), files, dates, etc.
|
|
41
|
+
const payload = serializeActionPayload(rawPayload, actionSchema.input_properties);
|
|
42
|
+
const config = configInstance.getConfig();
|
|
43
|
+
const backend = config.backendConfigs['default'];
|
|
44
|
+
if (!backend) {
|
|
45
|
+
throw new Error(`No backend configuration found for key: default`);
|
|
46
|
+
}
|
|
47
|
+
const baseUrl = backend.API_URL.replace(/\/+$/, '');
|
|
48
|
+
const actionUrl = `${baseUrl}/actions/send_notification/`;
|
|
49
|
+
const headers = backend.getAuthHeaders ? backend.getAuthHeaders() : {};
|
|
50
|
+
try {
|
|
51
|
+
const response = await axios.post(actionUrl, payload, {
|
|
52
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
53
|
+
...axiosOverrides,
|
|
54
|
+
});
|
|
55
|
+
return sendNotificationResponseSchema.parse(response.data);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
if (error instanceof z.ZodError) {
|
|
59
|
+
throw new Error(`Send Notification failed: Invalid response from server. Details: ${error.message}`);
|
|
60
|
+
}
|
|
61
|
+
if (error.response && error.response.data) {
|
|
62
|
+
const parsedError = parseStateZeroError(error.response.data);
|
|
63
|
+
if (Error.captureStackTrace) {
|
|
64
|
+
Error.captureStackTrace(parsedError, sendNotification);
|
|
65
|
+
}
|
|
66
|
+
throw parsedError;
|
|
67
|
+
}
|
|
68
|
+
else if (error.request) {
|
|
69
|
+
throw new Error(`Send Notification failed: No response received from server.`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new Error(`Send Notification failed: ${error.message}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export default sendNotification;
|
|
77
|
+
sendNotification.actionName = 'send_notification';
|
|
78
|
+
sendNotification.title = 'Send Notification';
|
|
79
|
+
sendNotification.app = 'django_app';
|
|
80
|
+
sendNotification.permissions = ['CanSendNotifications'];
|
|
81
|
+
sendNotification.configKey = 'default';
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
{
|
|
2
|
+
"action_name": "send_notification",
|
|
3
|
+
"app": "django_app",
|
|
4
|
+
"title": "Send Notification",
|
|
5
|
+
"docstring": "Send notifications to multiple recipients",
|
|
6
|
+
"class_name": "SendNotification",
|
|
7
|
+
"input_properties": {
|
|
8
|
+
"message": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"title": "Message",
|
|
11
|
+
"required": true,
|
|
12
|
+
"description": "Notification message to send",
|
|
13
|
+
"nullable": false,
|
|
14
|
+
"format": null,
|
|
15
|
+
"max_length": 500,
|
|
16
|
+
"choices": null,
|
|
17
|
+
"default": null,
|
|
18
|
+
"validators": [],
|
|
19
|
+
"max_digits": null,
|
|
20
|
+
"decimal_places": null,
|
|
21
|
+
"read_only": false,
|
|
22
|
+
"ref": null
|
|
23
|
+
},
|
|
24
|
+
"recipients": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"title": "Recipients",
|
|
27
|
+
"required": true,
|
|
28
|
+
"description": "List of email addresses to notify",
|
|
29
|
+
"nullable": false,
|
|
30
|
+
"format": null,
|
|
31
|
+
"max_length": null,
|
|
32
|
+
"choices": null,
|
|
33
|
+
"default": null,
|
|
34
|
+
"validators": [],
|
|
35
|
+
"max_digits": null,
|
|
36
|
+
"decimal_places": null,
|
|
37
|
+
"read_only": false,
|
|
38
|
+
"ref": null
|
|
39
|
+
},
|
|
40
|
+
"priority": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"title": "Priority",
|
|
43
|
+
"required": false,
|
|
44
|
+
"description": "Notification priority level",
|
|
45
|
+
"nullable": false,
|
|
46
|
+
"format": null,
|
|
47
|
+
"max_length": null,
|
|
48
|
+
"choices": {
|
|
49
|
+
"low": "Low",
|
|
50
|
+
"high": "High"
|
|
51
|
+
},
|
|
52
|
+
"default": "low",
|
|
53
|
+
"validators": [],
|
|
54
|
+
"max_digits": null,
|
|
55
|
+
"decimal_places": null,
|
|
56
|
+
"read_only": false,
|
|
57
|
+
"ref": null
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"response_properties": {
|
|
61
|
+
"success": {
|
|
62
|
+
"type": "boolean",
|
|
63
|
+
"title": "Success",
|
|
64
|
+
"required": true,
|
|
65
|
+
"description": null,
|
|
66
|
+
"nullable": false,
|
|
67
|
+
"format": null,
|
|
68
|
+
"max_length": null,
|
|
69
|
+
"choices": null,
|
|
70
|
+
"default": null,
|
|
71
|
+
"validators": [],
|
|
72
|
+
"max_digits": null,
|
|
73
|
+
"decimal_places": null,
|
|
74
|
+
"read_only": false,
|
|
75
|
+
"ref": null
|
|
76
|
+
},
|
|
77
|
+
"message_id": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"title": "Message id",
|
|
80
|
+
"required": true,
|
|
81
|
+
"description": null,
|
|
82
|
+
"nullable": false,
|
|
83
|
+
"format": null,
|
|
84
|
+
"max_length": null,
|
|
85
|
+
"choices": null,
|
|
86
|
+
"default": null,
|
|
87
|
+
"validators": [],
|
|
88
|
+
"max_digits": null,
|
|
89
|
+
"decimal_places": null,
|
|
90
|
+
"read_only": false,
|
|
91
|
+
"ref": null
|
|
92
|
+
},
|
|
93
|
+
"sent_to": {
|
|
94
|
+
"type": "integer",
|
|
95
|
+
"title": "Sent to",
|
|
96
|
+
"required": true,
|
|
97
|
+
"description": null,
|
|
98
|
+
"nullable": false,
|
|
99
|
+
"format": null,
|
|
100
|
+
"max_length": null,
|
|
101
|
+
"choices": null,
|
|
102
|
+
"default": null,
|
|
103
|
+
"validators": [],
|
|
104
|
+
"max_digits": null,
|
|
105
|
+
"decimal_places": null,
|
|
106
|
+
"read_only": false,
|
|
107
|
+
"ref": null
|
|
108
|
+
},
|
|
109
|
+
"queued_at": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"title": "Queued at",
|
|
112
|
+
"required": true,
|
|
113
|
+
"description": null,
|
|
114
|
+
"nullable": false,
|
|
115
|
+
"format": "date-time",
|
|
116
|
+
"max_length": null,
|
|
117
|
+
"choices": null,
|
|
118
|
+
"default": null,
|
|
119
|
+
"validators": [],
|
|
120
|
+
"max_digits": null,
|
|
121
|
+
"decimal_places": null,
|
|
122
|
+
"read_only": false,
|
|
123
|
+
"ref": null
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"relationships": {},
|
|
127
|
+
"permissions": [
|
|
128
|
+
"CanSendNotifications"
|
|
129
|
+
],
|
|
130
|
+
"display": {
|
|
131
|
+
"display_title": "Send Notifications",
|
|
132
|
+
"display_description": "Send notifications to multiple recipients with priority control",
|
|
133
|
+
"field_groups": [
|
|
134
|
+
{
|
|
135
|
+
"display_title": "Message Content",
|
|
136
|
+
"display_description": "The notification message and priority level",
|
|
137
|
+
"field_names": [
|
|
138
|
+
"message",
|
|
139
|
+
"priority"
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"display_title": "Recipients",
|
|
144
|
+
"display_description": "Email addresses to send notifications to",
|
|
145
|
+
"field_names": [
|
|
146
|
+
"recipients"
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
"field_display_configs": [
|
|
151
|
+
{
|
|
152
|
+
"field_name": "message",
|
|
153
|
+
"display_component": "TextArea",
|
|
154
|
+
"filter_queryset": null,
|
|
155
|
+
"display_help_text": "Enter your notification message here (max 500 characters)",
|
|
156
|
+
"extra": null
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"field_name": "priority",
|
|
160
|
+
"display_component": "RadioGroup",
|
|
161
|
+
"filter_queryset": null,
|
|
162
|
+
"display_help_text": "High priority notifications are processed first",
|
|
163
|
+
"extra": null
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"field_name": "recipients",
|
|
167
|
+
"display_component": "EmailListInput",
|
|
168
|
+
"filter_queryset": null,
|
|
169
|
+
"display_help_text": "Add one or more email addresses",
|
|
170
|
+
"extra": null
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
"extra": null
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./django_app/index.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './django_app/index.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./default/index.js";
|
|
@@ -7,14 +7,7 @@
|
|
|
7
7
|
* @returns {string[]} Array of dot-notation paths, e.g. ['author.id','createdAt.year']
|
|
8
8
|
*/
|
|
9
9
|
export function getRequiredFields(queryBuild: Object, ModelClass: Class): string[];
|
|
10
|
-
|
|
11
|
-
* Pick out only the required fields from a (possibly nested) model object.
|
|
12
|
-
*
|
|
13
|
-
* @param {string[]} requiredPaths – e.g. ['id','related.name','related.age']
|
|
14
|
-
* @param {Object} instance – e.g. { id: 3, related: { name: 'bob', age: 12, foo: 'bar' } }
|
|
15
|
-
* @returns {Object} – e.g. { id: 3, related: { name: 'bob', age: 12 } }
|
|
16
|
-
*/
|
|
17
|
-
export function pickRequiredFields(requiredPaths: string[], instance: Object): Object;
|
|
10
|
+
export function pickRequiredFields(requiredPaths: any, instance: any): {};
|
|
18
11
|
/**
|
|
19
12
|
* Filter and order a collection of data objects according to a QuerySet's AST.
|
|
20
13
|
* This combines getRequiredFields, pickRequiredFields, and processQuery in one function.
|
|
@@ -99,6 +99,30 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
|
|
|
99
99
|
const relationship = currentModel.relationshipFields.get(part);
|
|
100
100
|
const relatedModel = relationship.ModelClass();
|
|
101
101
|
const relationshipType = relationship.relationshipType;
|
|
102
|
+
// If this is not the last part and it's M2M, recursively process the remaining path
|
|
103
|
+
if (!isLastPart && relationshipType === 'many-to-many') {
|
|
104
|
+
// Build the remaining path including any lookup operators
|
|
105
|
+
const remainingFieldParts = fieldParts.slice(i + 1);
|
|
106
|
+
let fullRemainingPath = remainingFieldParts.join('__');
|
|
107
|
+
if (lookupChain.length > 0) {
|
|
108
|
+
fullRemainingPath += '__' + lookupChain.join('__');
|
|
109
|
+
}
|
|
110
|
+
else if (lookup) {
|
|
111
|
+
fullRemainingPath += '__' + lookup;
|
|
112
|
+
}
|
|
113
|
+
// Recursively process the remaining path with the related model
|
|
114
|
+
const innerResult = processFieldPath(fullRemainingPath, value, relatedModel, options);
|
|
115
|
+
// Build the required path for data picking (full dot-notation path)
|
|
116
|
+
const innerRequiredPath = innerResult.requiredPath || innerResult.field;
|
|
117
|
+
const requiredPath = `${part}.${innerRequiredPath}`;
|
|
118
|
+
// Wrap the inner result in $elemMatch for this M2M field
|
|
119
|
+
return {
|
|
120
|
+
field: part,
|
|
121
|
+
operator: { $elemMatch: { [innerResult.field]: innerResult.operator } },
|
|
122
|
+
isM2M: true,
|
|
123
|
+
requiredPath // Full path for data picking
|
|
124
|
+
};
|
|
125
|
+
}
|
|
102
126
|
// Add this relationship field to the path
|
|
103
127
|
processedPath.push(part);
|
|
104
128
|
// If this is not the last part, update the current model to the related model
|
|
@@ -168,6 +192,10 @@ function processFieldPath(fieldPath, value, ModelClass, options = {}) {
|
|
|
168
192
|
if (normalizedValue && typeof normalizedValue === 'object' && 'pk' in normalizedValue) {
|
|
169
193
|
raw = normalizedValue.pk;
|
|
170
194
|
}
|
|
195
|
+
// For M2M fields, use $elemMatch to check if any element's pk matches
|
|
196
|
+
if (isM2M) {
|
|
197
|
+
return { field: finalPath, operator: { $elemMatch: { pk: { $eq: raw } } }, isM2M: true };
|
|
198
|
+
}
|
|
171
199
|
return { field: finalPath, operator: { $eq: raw } };
|
|
172
200
|
}
|
|
173
201
|
// Default to direct equality
|
|
@@ -217,18 +245,19 @@ function createOperatorFromLookup(field, lookup, value, isRelationship, ModelCla
|
|
|
217
245
|
// For relationship fields with lookups, we need special handling
|
|
218
246
|
if (lookup === 'isnull') {
|
|
219
247
|
if (isM2M) {
|
|
220
|
-
// For M2M fields, isnull=True means null OR empty array
|
|
221
|
-
// that can mean the field was not fetched so we can't be sure
|
|
248
|
+
// For M2M fields, isnull=True means null OR empty array
|
|
222
249
|
// isnull=False means has at least one item
|
|
223
|
-
// Use
|
|
250
|
+
// Use document-level $where to check the array itself (not iterate elements)
|
|
251
|
+
const m2mField = field;
|
|
252
|
+
const checkEmpty = value;
|
|
224
253
|
return {
|
|
225
|
-
field,
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
254
|
+
field: '$where', // document-level operator
|
|
255
|
+
requiredPath: field, // need the M2M field itself for data picking
|
|
256
|
+
operator: function (doc) {
|
|
257
|
+
const fieldValue = doc[m2mField];
|
|
258
|
+
const isEmpty = fieldValue === null ||
|
|
259
|
+
(Array.isArray(fieldValue) && fieldValue.length === 0);
|
|
260
|
+
return checkEmpty ? isEmpty : !isEmpty;
|
|
232
261
|
}
|
|
233
262
|
};
|
|
234
263
|
}
|
|
@@ -239,10 +268,17 @@ function createOperatorFromLookup(field, lookup, value, isRelationship, ModelCla
|
|
|
239
268
|
};
|
|
240
269
|
}
|
|
241
270
|
else if (lookup === 'in') {
|
|
271
|
+
// For M2M, check if any element's pk is in the provided array
|
|
272
|
+
if (isM2M) {
|
|
273
|
+
return { field, operator: { $elemMatch: { pk: { $in: value } } }, isM2M: true };
|
|
274
|
+
}
|
|
242
275
|
return { field, operator: { $in: value } };
|
|
243
276
|
}
|
|
244
277
|
else {
|
|
245
278
|
// Default handling for relationship fields
|
|
279
|
+
if (isM2M) {
|
|
280
|
+
return { field, operator: { $elemMatch: { pk: { $eq: value } } }, isM2M: true };
|
|
281
|
+
}
|
|
246
282
|
return { field, operator: { $eq: value } };
|
|
247
283
|
}
|
|
248
284
|
}
|
|
@@ -545,10 +581,11 @@ function createFilterWithDateOperations(criteria, ModelClass) {
|
|
|
545
581
|
function convertToSiftCriteria(conditions, ModelClass) {
|
|
546
582
|
const result = {};
|
|
547
583
|
const datePartFilters = new Map(); // Map to collect date part filters by field
|
|
584
|
+
const m2mConditions = new Map(); // Map to collect M2M $elemMatch conditions by field
|
|
548
585
|
for (const [key, value] of Object.entries(conditions)) {
|
|
549
586
|
try {
|
|
550
587
|
const processedResult = processFieldPath(key, value, ModelClass);
|
|
551
|
-
const { field, operator, isDatePart } = processedResult;
|
|
588
|
+
const { field, operator, isDatePart, isM2M } = processedResult;
|
|
552
589
|
if (isDatePart) {
|
|
553
590
|
// Handle date part operators separately
|
|
554
591
|
if (!datePartFilters.has(field)) {
|
|
@@ -556,6 +593,13 @@ function convertToSiftCriteria(conditions, ModelClass) {
|
|
|
556
593
|
}
|
|
557
594
|
datePartFilters.get(field).push({ [field]: operator });
|
|
558
595
|
}
|
|
596
|
+
else if (isM2M && operator.$elemMatch) {
|
|
597
|
+
// Collect M2M conditions to merge into single $elemMatch
|
|
598
|
+
if (!m2mConditions.has(field)) {
|
|
599
|
+
m2mConditions.set(field, []);
|
|
600
|
+
}
|
|
601
|
+
m2mConditions.get(field).push(operator.$elemMatch);
|
|
602
|
+
}
|
|
559
603
|
else {
|
|
560
604
|
// For regular operators, merge if we already have criteria for this field
|
|
561
605
|
if (result[field]) {
|
|
@@ -570,6 +614,17 @@ function convertToSiftCriteria(conditions, ModelClass) {
|
|
|
570
614
|
throw new Error(`Failed to process field '${key}': ${error.message}`);
|
|
571
615
|
}
|
|
572
616
|
}
|
|
617
|
+
// Merge M2M conditions: all conditions on same M2M field go into single $elemMatch
|
|
618
|
+
// so the SAME related object must match ALL conditions
|
|
619
|
+
for (const [field, elemMatchConditions] of m2mConditions.entries()) {
|
|
620
|
+
if (elemMatchConditions.length === 1) {
|
|
621
|
+
result[field] = { $elemMatch: elemMatchConditions[0] };
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
// Multiple conditions - wrap in $and so same element must match all
|
|
625
|
+
result[field] = { $elemMatch: { $and: elemMatchConditions } };
|
|
626
|
+
}
|
|
627
|
+
}
|
|
573
628
|
// If we have date part filters, combine them with the result
|
|
574
629
|
if (datePartFilters.size > 0) {
|
|
575
630
|
const andConditions = [];
|
|
@@ -663,6 +718,9 @@ function convertFilterNodeToSiftCriteria(filterNode, ModelClass) {
|
|
|
663
718
|
return null;
|
|
664
719
|
if (childCriteria.length === 1)
|
|
665
720
|
return childCriteria[0];
|
|
721
|
+
// Chained filters use $and at top level - this gives ANY/ANY semantics for M2M
|
|
722
|
+
// (each $elemMatch can be satisfied by different elements)
|
|
723
|
+
// This matches Django's chained .filter() behavior
|
|
666
724
|
return { $and: childCriteria };
|
|
667
725
|
}
|
|
668
726
|
// For compound OR nodes
|
|
@@ -863,12 +921,24 @@ export function getRequiredFields(queryBuild, ModelClass) {
|
|
|
863
921
|
function addPath(rawKey) {
|
|
864
922
|
try {
|
|
865
923
|
// We pass `null` as the value, since we only care about .field
|
|
866
|
-
const { field } = processFieldPath(rawKey, null, ModelClass);
|
|
867
|
-
|
|
924
|
+
const { field, isM2M, requiredPath } = processFieldPath(rawKey, null, ModelClass);
|
|
925
|
+
// Use requiredPath if available (for M2M traversal), otherwise use field
|
|
926
|
+
// For M2M fields at the end of a path (no requiredPath), we need the pk field
|
|
927
|
+
let finalPath;
|
|
928
|
+
if (requiredPath) {
|
|
929
|
+
finalPath = requiredPath;
|
|
930
|
+
}
|
|
931
|
+
else if (isM2M) {
|
|
932
|
+
finalPath = `${field}.pk`;
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
finalPath = field;
|
|
936
|
+
}
|
|
937
|
+
paths.add(finalPath);
|
|
868
938
|
}
|
|
869
939
|
catch (err) {
|
|
870
|
-
// if a key doesn
|
|
871
|
-
console.warn(`getRequiredFields: couldn
|
|
940
|
+
// if a key doesn't map, warn and skip it
|
|
941
|
+
console.warn(`getRequiredFields: couldn't process "${rawKey}": ${err.message}`);
|
|
872
942
|
}
|
|
873
943
|
}
|
|
874
944
|
// Recursively walk your filter AST
|
|
@@ -917,42 +987,48 @@ export function getRequiredFields(queryBuild, ModelClass) {
|
|
|
917
987
|
* @param {Object} instance – e.g. { id: 3, related: { name: 'bob', age: 12, foo: 'bar' } }
|
|
918
988
|
* @returns {Object} – e.g. { id: 3, related: { name: 'bob', age: 12 } }
|
|
919
989
|
*/
|
|
990
|
+
/**
|
|
991
|
+
* Recursively sets a value in a result object following a path.
|
|
992
|
+
* Handles arrays (M2M fields) by mapping over each element.
|
|
993
|
+
*/
|
|
994
|
+
function setNestedValueRecursive(result, source, pathParts) {
|
|
995
|
+
if (source == null || pathParts.length === 0) {
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
const [current, ...rest] = pathParts;
|
|
999
|
+
const sourceValue = source[current];
|
|
1000
|
+
if (sourceValue === undefined) {
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
1003
|
+
if (rest.length === 0) {
|
|
1004
|
+
// Last part - set the value directly, keeping M2M as full objects
|
|
1005
|
+
result[current] = sourceValue;
|
|
1006
|
+
}
|
|
1007
|
+
else if (Array.isArray(sourceValue)) {
|
|
1008
|
+
// M2M array with nested path - recursively extract from each element
|
|
1009
|
+
if (!(current in result)) {
|
|
1010
|
+
result[current] = [];
|
|
1011
|
+
}
|
|
1012
|
+
sourceValue.forEach((item, idx) => {
|
|
1013
|
+
if (result[current][idx] === undefined) {
|
|
1014
|
+
result[current][idx] = {};
|
|
1015
|
+
}
|
|
1016
|
+
setNestedValueRecursive(result[current][idx], item, rest);
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
else if (typeof sourceValue === 'object') {
|
|
1020
|
+
// Regular nested object (FK)
|
|
1021
|
+
if (!(current in result)) {
|
|
1022
|
+
result[current] = {};
|
|
1023
|
+
}
|
|
1024
|
+
setNestedValueRecursive(result[current], sourceValue, rest);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
920
1027
|
export function pickRequiredFields(requiredPaths, instance) {
|
|
921
1028
|
const result = {};
|
|
922
1029
|
requiredPaths.forEach(path => {
|
|
923
1030
|
const parts = path.split('.');
|
|
924
|
-
|
|
925
|
-
let value = instance;
|
|
926
|
-
for (const key of parts) {
|
|
927
|
-
if (value == null || !(key in value)) {
|
|
928
|
-
value = undefined;
|
|
929
|
-
break;
|
|
930
|
-
}
|
|
931
|
-
value = value[key];
|
|
932
|
-
}
|
|
933
|
-
if (value === undefined) {
|
|
934
|
-
// skip missing
|
|
935
|
-
return;
|
|
936
|
-
}
|
|
937
|
-
// Convert M2M arrays from Model instances to PKs for comparison
|
|
938
|
-
// This handles the case where the live representation returns [Model1, Model2]
|
|
939
|
-
// but we need [pk1, pk2] for sift filtering
|
|
940
|
-
if (Array.isArray(value) && value.length > 0 && value[0]?.pk !== undefined) {
|
|
941
|
-
value = value.map(item => item.pk);
|
|
942
|
-
}
|
|
943
|
-
// Build nested structure in the result
|
|
944
|
-
let cursor = result;
|
|
945
|
-
parts.forEach((key, idx) => {
|
|
946
|
-
if (idx === parts.length - 1) {
|
|
947
|
-
cursor[key] = value;
|
|
948
|
-
}
|
|
949
|
-
else {
|
|
950
|
-
if (!(key in cursor)) {
|
|
951
|
-
cursor[key] = {};
|
|
952
|
-
}
|
|
953
|
-
cursor = cursor[key];
|
|
954
|
-
}
|
|
955
|
-
});
|
|
1031
|
+
setNestedValueRecursive(result, instance, parts);
|
|
956
1032
|
});
|
|
957
1033
|
return result;
|
|
958
1034
|
}
|
|
@@ -83,7 +83,10 @@ export class DateParsingHelpers {
|
|
|
83
83
|
try {
|
|
84
84
|
// Handle ISO format (Django's default)
|
|
85
85
|
if (!dateFormat || dateFormat === 'iso-8601') {
|
|
86
|
-
return date
|
|
86
|
+
// For date-only fields, return just the date portion
|
|
87
|
+
return fieldFormat === 'date'
|
|
88
|
+
? date.toISOString().slice(0, 10)
|
|
89
|
+
: date.toISOString();
|
|
87
90
|
}
|
|
88
91
|
// Handle supported Django formats
|
|
89
92
|
const dateFnsFormat = this.SUPPORTED_FORMATS[dateFormat];
|
|
@@ -75,9 +75,13 @@ export const dateFieldSerializer = {
|
|
|
75
75
|
return value;
|
|
76
76
|
// If it's a Date object, serialize it using DateParsingHelpers
|
|
77
77
|
if (value instanceof Date) {
|
|
78
|
-
const { model, field } = context;
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
const { model, field, fieldSchema } = context;
|
|
79
|
+
// Use model schema, or create minimal schema from fieldSchema (for actions)
|
|
80
|
+
const schema = model?.schema || (fieldSchema?.format && {
|
|
81
|
+
properties: { [field]: { format: fieldSchema.format } }
|
|
82
|
+
});
|
|
83
|
+
if (schema) {
|
|
84
|
+
return DateParsingHelpers.serializeDate(value, field, schema);
|
|
81
85
|
}
|
|
82
86
|
// Fallback if no schema context
|
|
83
87
|
return value.toISOString();
|
|
@@ -214,7 +218,7 @@ export function serializeActionPayload(payload, inputProperties) {
|
|
|
214
218
|
const typeSerializers = serializers[type];
|
|
215
219
|
const serializer = typeSerializers?.[format];
|
|
216
220
|
if (serializer) {
|
|
217
|
-
serializedPayload[field] = serializer.toInternal(value, { field });
|
|
221
|
+
serializedPayload[field] = serializer.toInternal(value, { field, fieldSchema });
|
|
218
222
|
}
|
|
219
223
|
else {
|
|
220
224
|
serializedPayload[field] = value;
|