@chanaka_nakandala/integration-agent 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.
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/dist/cache/file-cache.d.ts +10 -0
- package/dist/cache/file-cache.d.ts.map +1 -0
- package/dist/cache/file-cache.js +79 -0
- package/dist/cache/file-cache.js.map +1 -0
- package/dist/cli/init-command.d.ts +2 -0
- package/dist/cli/init-command.d.ts.map +1 -0
- package/dist/cli/init-command.js +115 -0
- package/dist/cli/init-command.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +493 -0
- package/dist/index.js.map +1 -0
- package/dist/scrapers/docs-scraper.d.ts +28 -0
- package/dist/scrapers/docs-scraper.d.ts.map +1 -0
- package/dist/scrapers/docs-scraper.js +200 -0
- package/dist/scrapers/docs-scraper.js.map +1 -0
- package/dist/search/keyword-search.d.ts +39 -0
- package/dist/search/keyword-search.d.ts.map +1 -0
- package/dist/search/keyword-search.js +127 -0
- package/dist/search/keyword-search.js.map +1 -0
- package/dist/tools/code-generation-tool.d.ts +33 -0
- package/dist/tools/code-generation-tool.d.ts.map +1 -0
- package/dist/tools/code-generation-tool.js +62 -0
- package/dist/tools/code-generation-tool.js.map +1 -0
- package/dist/tools/search-result-formatter.d.ts +27 -0
- package/dist/tools/search-result-formatter.d.ts.map +1 -0
- package/dist/tools/search-result-formatter.js +89 -0
- package/dist/tools/search-result-formatter.js.map +1 -0
- package/dist/tools/search-tool.d.ts +9 -0
- package/dist/tools/search-tool.d.ts.map +1 -0
- package/dist/tools/search-tool.js +32 -0
- package/dist/tools/search-tool.js.map +1 -0
- package/dist/tools/template-loader.d.ts +54 -0
- package/dist/tools/template-loader.d.ts.map +1 -0
- package/dist/tools/template-loader.js +148 -0
- package/dist/tools/template-loader.js.map +1 -0
- package/dist/types/search.d.ts +21 -0
- package/dist/types/search.d.ts.map +1 -0
- package/dist/types/search.js +2 -0
- package/dist/types/search.js.map +1 -0
- package/package.json +63 -0
- package/templates/README.md +98 -0
- package/templates/authenticate/curl.template +97 -0
- package/templates/authenticate/java.template +155 -0
- package/templates/authenticate/python.template +111 -0
- package/templates/authenticate/typescript.template +98 -0
- package/templates/create_menu/curl.template +145 -0
- package/templates/create_menu/java.template +285 -0
- package/templates/create_menu/python.template +159 -0
- package/templates/create_menu/typescript.template +184 -0
- package/templates/receive_order/curl.template +138 -0
- package/templates/receive_order/java.template +263 -0
- package/templates/receive_order/python.template +157 -0
- package/templates/receive_order/typescript.template +194 -0
- package/templates/update_item_availability/curl.template +143 -0
- package/templates/update_item_availability/java.template +279 -0
- package/templates/update_item_availability/python.template +203 -0
- package/templates/update_item_availability/typescript.template +194 -0
- package/templates/update_order_status/curl.template +138 -0
- package/templates/update_order_status/java.template +202 -0
- package/templates/update_order_status/python.template +142 -0
- package/templates/update_order_status/typescript.template +139 -0
@@ -0,0 +1,194 @@
|
|
1
|
+
/**
|
2
|
+
* Grubtech Order Receive Webhook - TypeScript (Express.js)
|
3
|
+
*
|
4
|
+
* This example demonstrates how to receive orders from Grubtech
|
5
|
+
* via webhook and process them in your system.
|
6
|
+
*
|
7
|
+
* Prerequisites:
|
8
|
+
* - Node.js 18+
|
9
|
+
* - Express.js (npm install express)
|
10
|
+
* - crypto module (built-in)
|
11
|
+
*
|
12
|
+
* Replace the following placeholders:
|
13
|
+
* - {{WEBHOOK_SECRET}}: Your webhook secret for signature verification
|
14
|
+
* - {{ORDER_PROCESSING_LOGIC}}: Your business logic to process the order
|
15
|
+
*/
|
16
|
+
|
17
|
+
import express, { Request, Response } from 'express';
|
18
|
+
import crypto from 'crypto';
|
19
|
+
|
20
|
+
const app = express();
|
21
|
+
const PORT = 3000;
|
22
|
+
const WEBHOOK_SECRET = '{{WEBHOOK_SECRET}}';
|
23
|
+
|
24
|
+
// Middleware to parse JSON and preserve raw body for signature verification
|
25
|
+
app.use(
|
26
|
+
express.json({
|
27
|
+
verify: (req: any, res, buf) => {
|
28
|
+
req.rawBody = buf.toString('utf8');
|
29
|
+
},
|
30
|
+
})
|
31
|
+
);
|
32
|
+
|
33
|
+
interface OrderItem {
|
34
|
+
id: string;
|
35
|
+
name: string;
|
36
|
+
quantity: number;
|
37
|
+
price: number;
|
38
|
+
modifiers?: Array<{ id: string; name: string; price: number }>;
|
39
|
+
}
|
40
|
+
|
41
|
+
interface Order {
|
42
|
+
orderId: string;
|
43
|
+
partnerOrderId?: string;
|
44
|
+
status: string;
|
45
|
+
createdAt: string;
|
46
|
+
customer: {
|
47
|
+
name: string;
|
48
|
+
phone: string;
|
49
|
+
email?: string;
|
50
|
+
};
|
51
|
+
delivery: {
|
52
|
+
type: 'delivery' | 'pickup';
|
53
|
+
address?: string;
|
54
|
+
city?: string;
|
55
|
+
instructions?: string;
|
56
|
+
};
|
57
|
+
items: OrderItem[];
|
58
|
+
totals: {
|
59
|
+
subtotal: number;
|
60
|
+
tax: number;
|
61
|
+
deliveryFee: number;
|
62
|
+
total: number;
|
63
|
+
};
|
64
|
+
payment: {
|
65
|
+
method: string;
|
66
|
+
status: string;
|
67
|
+
};
|
68
|
+
}
|
69
|
+
|
70
|
+
interface WebhookResponse {
|
71
|
+
accepted: boolean;
|
72
|
+
orderId?: string;
|
73
|
+
partnerOrderId?: string;
|
74
|
+
message?: string;
|
75
|
+
estimatedReadyTime?: string;
|
76
|
+
}
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Verify webhook signature
|
80
|
+
*/
|
81
|
+
function verifySignature(req: any): boolean {
|
82
|
+
const signature = req.headers['x-grubtech-signature'];
|
83
|
+
|
84
|
+
if (!signature) {
|
85
|
+
console.error('❌ No signature provided');
|
86
|
+
return false;
|
87
|
+
}
|
88
|
+
|
89
|
+
// Calculate expected signature using HMAC-SHA256
|
90
|
+
const expectedSignature = crypto
|
91
|
+
.createHmac('sha256', WEBHOOK_SECRET)
|
92
|
+
.update(req.rawBody)
|
93
|
+
.digest('hex');
|
94
|
+
|
95
|
+
// Constant-time comparison to prevent timing attacks
|
96
|
+
const isValid = crypto.timingSafeEqual(
|
97
|
+
Buffer.from(signature),
|
98
|
+
Buffer.from(expectedSignature)
|
99
|
+
);
|
100
|
+
|
101
|
+
if (!isValid) {
|
102
|
+
console.error('❌ Invalid signature');
|
103
|
+
}
|
104
|
+
|
105
|
+
return isValid;
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Process order (replace with your business logic)
|
110
|
+
*/
|
111
|
+
async function processOrder(order: Order): Promise<WebhookResponse> {
|
112
|
+
try {
|
113
|
+
console.log(`📦 Processing order: ${order.orderId}`);
|
114
|
+
console.log(`Customer: ${order.customer.name}`);
|
115
|
+
console.log(`Items: ${order.items.length}`);
|
116
|
+
console.log(`Total: $${order.totals.total}`);
|
117
|
+
|
118
|
+
// {{ORDER_PROCESSING_LOGIC}}
|
119
|
+
// Example: Save to database, notify kitchen, etc.
|
120
|
+
|
121
|
+
// Simulate processing time
|
122
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
123
|
+
|
124
|
+
// Calculate estimated ready time (example: 30 minutes from now)
|
125
|
+
const readyTime = new Date(Date.now() + 30 * 60 * 1000).toISOString();
|
126
|
+
|
127
|
+
return {
|
128
|
+
accepted: true,
|
129
|
+
orderId: order.orderId,
|
130
|
+
partnerOrderId: `POS-${Date.now()}`,
|
131
|
+
message: 'Order accepted successfully',
|
132
|
+
estimatedReadyTime: readyTime,
|
133
|
+
};
|
134
|
+
} catch (error) {
|
135
|
+
console.error('❌ Order processing error:', error);
|
136
|
+
|
137
|
+
return {
|
138
|
+
accepted: false,
|
139
|
+
orderId: order.orderId,
|
140
|
+
message: `Order rejected: ${error}`,
|
141
|
+
};
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
/**
|
146
|
+
* Webhook endpoint to receive orders from Grubtech
|
147
|
+
*/
|
148
|
+
app.post('/webhooks/grubtech/orders', async (req: Request, res: Response) => {
|
149
|
+
try {
|
150
|
+
// Verify signature
|
151
|
+
if (!verifySignature(req)) {
|
152
|
+
return res.status(401).json({
|
153
|
+
accepted: false,
|
154
|
+
message: 'Invalid signature',
|
155
|
+
});
|
156
|
+
}
|
157
|
+
|
158
|
+
const order: Order = req.body;
|
159
|
+
|
160
|
+
// Validate order data
|
161
|
+
if (!order.orderId || !order.items || order.items.length === 0) {
|
162
|
+
return res.status(400).json({
|
163
|
+
accepted: false,
|
164
|
+
message: 'Invalid order data',
|
165
|
+
});
|
166
|
+
}
|
167
|
+
|
168
|
+
// Process order
|
169
|
+
const response = await processOrder(order);
|
170
|
+
|
171
|
+
// Send response
|
172
|
+
const statusCode = response.accepted ? 200 : 400;
|
173
|
+
return res.status(statusCode).json(response);
|
174
|
+
} catch (error) {
|
175
|
+
console.error('❌ Webhook error:', error);
|
176
|
+
return res.status(500).json({
|
177
|
+
accepted: false,
|
178
|
+
message: 'Internal server error',
|
179
|
+
});
|
180
|
+
}
|
181
|
+
});
|
182
|
+
|
183
|
+
/**
|
184
|
+
* Health check endpoint
|
185
|
+
*/
|
186
|
+
app.get('/health', (req: Request, res: Response) => {
|
187
|
+
res.json({ status: 'ok' });
|
188
|
+
});
|
189
|
+
|
190
|
+
// Start server
|
191
|
+
app.listen(PORT, () => {
|
192
|
+
console.log(`✅ Webhook server listening on port ${PORT}`);
|
193
|
+
console.log(`Webhook URL: http://localhost:${PORT}/webhooks/grubtech/orders`);
|
194
|
+
});
|
@@ -0,0 +1,143 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Grubtech Item Availability Update - cURL
|
4
|
+
#
|
5
|
+
# This example demonstrates how to update menu item availability in Grubtech.
|
6
|
+
#
|
7
|
+
# Prerequisites:
|
8
|
+
# - curl command-line tool
|
9
|
+
# - jq (for JSON parsing, optional)
|
10
|
+
# - Valid access token from authentication
|
11
|
+
#
|
12
|
+
# Replace the following placeholders:
|
13
|
+
# - {{AUTH_TOKEN}}: Access token from authentication step
|
14
|
+
# - {{ITEM_ID}}: The menu item ID to update
|
15
|
+
# - {{AVAILABLE_STATUS}}: Boolean availability status (true/false)
|
16
|
+
|
17
|
+
AUTH_TOKEN="{{AUTH_TOKEN}}"
|
18
|
+
ITEM_ID="{{ITEM_ID}}"
|
19
|
+
AVAILABLE_STATUS={{AVAILABLE_STATUS}} # true or false
|
20
|
+
BASE_URL="https://api.grubtech.io"
|
21
|
+
|
22
|
+
# Update single item availability
|
23
|
+
update_item_availability() {
|
24
|
+
local item_id=$1
|
25
|
+
local available=$2
|
26
|
+
local reason=$3
|
27
|
+
|
28
|
+
echo "Updating item ${item_id}: available=${available}"
|
29
|
+
|
30
|
+
# Construct payload
|
31
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
32
|
+
PAYLOAD=$(cat <<EOF
|
33
|
+
{
|
34
|
+
"itemId": "${item_id}",
|
35
|
+
"available": ${available},
|
36
|
+
"timestamp": "${TIMESTAMP}",
|
37
|
+
"reason": "${reason}"
|
38
|
+
}
|
39
|
+
EOF
|
40
|
+
)
|
41
|
+
|
42
|
+
# Make availability update request
|
43
|
+
RESPONSE=$(curl -s -w "\n%{http_code}" -X PUT \
|
44
|
+
"${BASE_URL}/v1/items/${item_id}/availability" \
|
45
|
+
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
46
|
+
-H "Content-Type: application/json" \
|
47
|
+
-d "$PAYLOAD")
|
48
|
+
|
49
|
+
# Extract HTTP status code and body
|
50
|
+
HTTP_CODE=$(echo "$RESPONSE" | tail -n 1)
|
51
|
+
BODY=$(echo "$RESPONSE" | sed '$d')
|
52
|
+
|
53
|
+
# Check for errors
|
54
|
+
if [ "$HTTP_CODE" -ne 200 ]; then
|
55
|
+
echo "❌ Availability update failed: HTTP $HTTP_CODE"
|
56
|
+
echo "Response: $BODY"
|
57
|
+
return 1
|
58
|
+
fi
|
59
|
+
|
60
|
+
echo "✅ Item availability updated successfully!"
|
61
|
+
echo "$BODY" | jq '.' 2>/dev/null || echo "$BODY"
|
62
|
+
}
|
63
|
+
|
64
|
+
# Update multiple items in bulk
|
65
|
+
update_bulk_availability() {
|
66
|
+
echo "Updating multiple items availability..."
|
67
|
+
|
68
|
+
# Construct bulk payload
|
69
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
70
|
+
PAYLOAD=$(cat <<'EOF'
|
71
|
+
{
|
72
|
+
"updates": [
|
73
|
+
{
|
74
|
+
"itemId": "item-123",
|
75
|
+
"available": false,
|
76
|
+
"reason": "Out of stock"
|
77
|
+
},
|
78
|
+
{
|
79
|
+
"itemId": "item-456",
|
80
|
+
"available": true,
|
81
|
+
"reason": "Back in stock"
|
82
|
+
},
|
83
|
+
{
|
84
|
+
"itemId": "item-789",
|
85
|
+
"available": true,
|
86
|
+
"reason": "In stock"
|
87
|
+
}
|
88
|
+
],
|
89
|
+
"timestamp": "TIMESTAMP_PLACEHOLDER"
|
90
|
+
}
|
91
|
+
EOF
|
92
|
+
)
|
93
|
+
|
94
|
+
# Replace timestamp placeholder
|
95
|
+
PAYLOAD=$(echo "$PAYLOAD" | sed "s/TIMESTAMP_PLACEHOLDER/$TIMESTAMP/")
|
96
|
+
|
97
|
+
# Make bulk availability update request
|
98
|
+
RESPONSE=$(curl -s -w "\n%{http_code}" -X PUT \
|
99
|
+
"${BASE_URL}/v1/items/availability/bulk" \
|
100
|
+
-H "Authorization: Bearer ${AUTH_TOKEN}" \
|
101
|
+
-H "Content-Type: application/json" \
|
102
|
+
-d "$PAYLOAD")
|
103
|
+
|
104
|
+
# Extract HTTP status code and body
|
105
|
+
HTTP_CODE=$(echo "$RESPONSE" | tail -n 1)
|
106
|
+
BODY=$(echo "$RESPONSE" | sed '$d')
|
107
|
+
|
108
|
+
# Check for errors
|
109
|
+
if [ "$HTTP_CODE" -ne 200 ]; then
|
110
|
+
echo "❌ Bulk availability update failed: HTTP $HTTP_CODE"
|
111
|
+
echo "Response: $BODY"
|
112
|
+
return 1
|
113
|
+
fi
|
114
|
+
|
115
|
+
echo "✅ Bulk availability updated successfully!"
|
116
|
+
echo "$BODY" | jq '.' 2>/dev/null || echo "$BODY"
|
117
|
+
}
|
118
|
+
|
119
|
+
# Helper functions
|
120
|
+
mark_out_of_stock() {
|
121
|
+
update_item_availability "$1" false "Out of stock"
|
122
|
+
}
|
123
|
+
|
124
|
+
mark_in_stock() {
|
125
|
+
update_item_availability "$1" true "Back in stock"
|
126
|
+
}
|
127
|
+
|
128
|
+
# Main execution
|
129
|
+
main() {
|
130
|
+
# Example 1: Single item update
|
131
|
+
update_item_availability "$ITEM_ID" "$AVAILABLE_STATUS" "Inventory sync"
|
132
|
+
|
133
|
+
echo ""
|
134
|
+
|
135
|
+
# Example 2: Mark item out of stock
|
136
|
+
# mark_out_of_stock "item-123"
|
137
|
+
|
138
|
+
# Example 3: Bulk update
|
139
|
+
# update_bulk_availability
|
140
|
+
}
|
141
|
+
|
142
|
+
# Run main function
|
143
|
+
main
|
@@ -0,0 +1,279 @@
|
|
1
|
+
/**
|
2
|
+
* Grubtech Item Availability Update - Java (Spring Boot)
|
3
|
+
*
|
4
|
+
* This example demonstrates how to update menu item availability in Grubtech
|
5
|
+
* to keep items in sync with your inventory status.
|
6
|
+
*
|
7
|
+
* Prerequisites:
|
8
|
+
* - Java 17+
|
9
|
+
* - Spring Boot 3.x
|
10
|
+
* - Spring WebFlux (for WebClient)
|
11
|
+
*
|
12
|
+
* Replace the following placeholders:
|
13
|
+
* - {{AUTH_TOKEN}}: Access token from authentication step
|
14
|
+
* - {{ITEM_ID}}: The menu item ID to update
|
15
|
+
* - {{AVAILABLE_STATUS}}: Boolean availability status (true/false)
|
16
|
+
*/
|
17
|
+
|
18
|
+
package com.example.grubtech;
|
19
|
+
|
20
|
+
import org.springframework.http.HttpHeaders;
|
21
|
+
import org.springframework.http.MediaType;
|
22
|
+
import org.springframework.stereotype.Service;
|
23
|
+
import org.springframework.web.reactive.function.client.WebClient;
|
24
|
+
import reactor.core.publisher.Mono;
|
25
|
+
|
26
|
+
import java.time.Instant;
|
27
|
+
import java.util.HashMap;
|
28
|
+
import java.util.List;
|
29
|
+
import java.util.Map;
|
30
|
+
import java.util.stream.Collectors;
|
31
|
+
|
32
|
+
@Service
|
33
|
+
public class GrubtechItemAvailabilityService {
|
34
|
+
|
35
|
+
private static final String AUTH_TOKEN = "{{AUTH_TOKEN}}";
|
36
|
+
private static final String BASE_URL = "https://api.grubtech.io";
|
37
|
+
|
38
|
+
private final WebClient webClient;
|
39
|
+
|
40
|
+
public GrubtechItemAvailabilityService() {
|
41
|
+
this.webClient = WebClient.builder()
|
42
|
+
.baseUrl(BASE_URL)
|
43
|
+
.build();
|
44
|
+
}
|
45
|
+
|
46
|
+
/**
|
47
|
+
* Update single item availability
|
48
|
+
*
|
49
|
+
* @param itemId The menu item ID
|
50
|
+
* @param available True if in stock, False if out of stock
|
51
|
+
* @param reason Optional reason for status change
|
52
|
+
* @return Availability update response
|
53
|
+
*/
|
54
|
+
public AvailabilityResponse updateItemAvailability(
|
55
|
+
String itemId,
|
56
|
+
boolean available,
|
57
|
+
String reason
|
58
|
+
) {
|
59
|
+
try {
|
60
|
+
// Construct payload
|
61
|
+
Map<String, Object> payload = new HashMap<>();
|
62
|
+
payload.put("itemId", itemId);
|
63
|
+
payload.put("available", available);
|
64
|
+
payload.put("timestamp", Instant.now().toString());
|
65
|
+
|
66
|
+
if (reason != null && !reason.isEmpty()) {
|
67
|
+
payload.put("reason", reason);
|
68
|
+
}
|
69
|
+
|
70
|
+
System.out.println("Updating item " + itemId + ": available=" + available);
|
71
|
+
|
72
|
+
// Make availability update request
|
73
|
+
AvailabilityResponse response = webClient.put()
|
74
|
+
.uri("/v1/items/" + itemId + "/availability")
|
75
|
+
.header(HttpHeaders.AUTHORIZATION, "Bearer " + AUTH_TOKEN)
|
76
|
+
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
77
|
+
.bodyValue(payload)
|
78
|
+
.retrieve()
|
79
|
+
.onStatus(
|
80
|
+
status -> !status.is2xxSuccessful(),
|
81
|
+
clientResponse -> clientResponse.bodyToMono(String.class)
|
82
|
+
.flatMap(errorBody -> Mono.error(
|
83
|
+
new RuntimeException(
|
84
|
+
"Availability update failed: " +
|
85
|
+
clientResponse.statusCode() +
|
86
|
+
" - " + errorBody
|
87
|
+
)
|
88
|
+
))
|
89
|
+
)
|
90
|
+
.bodyToMono(AvailabilityResponse.class)
|
91
|
+
.block();
|
92
|
+
|
93
|
+
if (response == null) {
|
94
|
+
throw new RuntimeException("No response received");
|
95
|
+
}
|
96
|
+
|
97
|
+
System.out.println("✅ Item availability updated successfully!");
|
98
|
+
System.out.println("Item ID: " + response.getItemId());
|
99
|
+
System.out.println("Available: " + available);
|
100
|
+
|
101
|
+
return response;
|
102
|
+
|
103
|
+
} catch (Exception e) {
|
104
|
+
System.err.println("❌ Availability update error: " + e.getMessage());
|
105
|
+
throw e;
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
/**
|
110
|
+
* Update multiple items availability in bulk
|
111
|
+
*
|
112
|
+
* @param updates List of item availability updates
|
113
|
+
* @return Bulk availability update response
|
114
|
+
*/
|
115
|
+
public BulkAvailabilityResponse updateBulkAvailability(
|
116
|
+
List<ItemUpdate> updates
|
117
|
+
) {
|
118
|
+
try {
|
119
|
+
// Construct bulk payload
|
120
|
+
Map<String, Object> payload = new HashMap<>();
|
121
|
+
payload.put("updates", updates);
|
122
|
+
payload.put("timestamp", Instant.now().toString());
|
123
|
+
|
124
|
+
System.out.println("Updating " + updates.size() + " items availability");
|
125
|
+
|
126
|
+
// Make bulk availability update request
|
127
|
+
BulkAvailabilityResponse response = webClient.put()
|
128
|
+
.uri("/v1/items/availability/bulk")
|
129
|
+
.header(HttpHeaders.AUTHORIZATION, "Bearer " + AUTH_TOKEN)
|
130
|
+
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
131
|
+
.bodyValue(payload)
|
132
|
+
.retrieve()
|
133
|
+
.onStatus(
|
134
|
+
status -> !status.is2xxSuccessful(),
|
135
|
+
clientResponse -> Mono.error(
|
136
|
+
new RuntimeException(
|
137
|
+
"Bulk availability update failed: " +
|
138
|
+
clientResponse.statusCode()
|
139
|
+
)
|
140
|
+
)
|
141
|
+
)
|
142
|
+
.bodyToMono(BulkAvailabilityResponse.class)
|
143
|
+
.block();
|
144
|
+
|
145
|
+
if (response == null) {
|
146
|
+
throw new RuntimeException("No response received");
|
147
|
+
}
|
148
|
+
|
149
|
+
System.out.println("✅ Bulk availability updated successfully!");
|
150
|
+
System.out.println("Updated: " + response.getUpdatedCount() + " items");
|
151
|
+
|
152
|
+
// Report any errors
|
153
|
+
if (response.getErrors() != null && !response.getErrors().isEmpty()) {
|
154
|
+
System.out.println("⚠️ " + response.getErrors().size() + " items had errors:");
|
155
|
+
response.getErrors().forEach(err ->
|
156
|
+
System.out.println(" - " + err.getItemId() + ": " + err.getError())
|
157
|
+
);
|
158
|
+
}
|
159
|
+
|
160
|
+
return response;
|
161
|
+
|
162
|
+
} catch (Exception e) {
|
163
|
+
System.err.println("❌ Bulk availability update error: " + e.getMessage());
|
164
|
+
throw e;
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
/**
|
169
|
+
* Mark item as out of stock
|
170
|
+
*/
|
171
|
+
public AvailabilityResponse markOutOfStock(String itemId) {
|
172
|
+
return updateItemAvailability(itemId, false, "Out of stock");
|
173
|
+
}
|
174
|
+
|
175
|
+
/**
|
176
|
+
* Mark item as back in stock
|
177
|
+
*/
|
178
|
+
public AvailabilityResponse markInStock(String itemId) {
|
179
|
+
return updateItemAvailability(itemId, true, "Back in stock");
|
180
|
+
}
|
181
|
+
|
182
|
+
/**
|
183
|
+
* Sync inventory with Grubtech
|
184
|
+
*/
|
185
|
+
public BulkAvailabilityResponse syncInventory(Map<String, Boolean> inventory) {
|
186
|
+
List<ItemUpdate> updates = inventory.entrySet().stream()
|
187
|
+
.map(entry -> new ItemUpdate(
|
188
|
+
entry.getKey(),
|
189
|
+
entry.getValue(),
|
190
|
+
entry.getValue() ? "In stock" : "Out of stock"
|
191
|
+
))
|
192
|
+
.collect(Collectors.toList());
|
193
|
+
|
194
|
+
return updateBulkAvailability(updates);
|
195
|
+
}
|
196
|
+
|
197
|
+
// Data models
|
198
|
+
public static class ItemUpdate {
|
199
|
+
private String itemId;
|
200
|
+
private boolean available;
|
201
|
+
private String reason;
|
202
|
+
|
203
|
+
public ItemUpdate(String itemId, boolean available, String reason) {
|
204
|
+
this.itemId = itemId;
|
205
|
+
this.available = available;
|
206
|
+
this.reason = reason;
|
207
|
+
}
|
208
|
+
|
209
|
+
// Getters
|
210
|
+
public String getItemId() { return itemId; }
|
211
|
+
public boolean isAvailable() { return available; }
|
212
|
+
public String getReason() { return reason; }
|
213
|
+
}
|
214
|
+
|
215
|
+
public static class AvailabilityResponse {
|
216
|
+
private boolean success;
|
217
|
+
private String itemId;
|
218
|
+
private String message;
|
219
|
+
|
220
|
+
// Getters and setters
|
221
|
+
public boolean isSuccess() { return success; }
|
222
|
+
public void setSuccess(boolean success) { this.success = success; }
|
223
|
+
public String getItemId() { return itemId; }
|
224
|
+
public void setItemId(String itemId) { this.itemId = itemId; }
|
225
|
+
public String getMessage() { return message; }
|
226
|
+
public void setMessage(String message) { this.message = message; }
|
227
|
+
}
|
228
|
+
|
229
|
+
public static class BulkAvailabilityResponse {
|
230
|
+
private boolean success;
|
231
|
+
private int updatedCount;
|
232
|
+
private List<ItemError> errors;
|
233
|
+
|
234
|
+
// Getters and setters
|
235
|
+
public boolean isSuccess() { return success; }
|
236
|
+
public void setSuccess(boolean success) { this.success = success; }
|
237
|
+
public int getUpdatedCount() { return updatedCount; }
|
238
|
+
public void setUpdatedCount(int updatedCount) { this.updatedCount = updatedCount; }
|
239
|
+
public List<ItemError> getErrors() { return errors; }
|
240
|
+
public void setErrors(List<ItemError> errors) { this.errors = errors; }
|
241
|
+
}
|
242
|
+
|
243
|
+
public static class ItemError {
|
244
|
+
private String itemId;
|
245
|
+
private String error;
|
246
|
+
|
247
|
+
// Getters and setters
|
248
|
+
public String getItemId() { return itemId; }
|
249
|
+
public void setItemId(String itemId) { this.itemId = itemId; }
|
250
|
+
public String getError() { return error; }
|
251
|
+
public void setError(String error) { this.error = error; }
|
252
|
+
}
|
253
|
+
|
254
|
+
// Main method for testing
|
255
|
+
public static void main(String[] args) {
|
256
|
+
try {
|
257
|
+
GrubtechItemAvailabilityService service = new GrubtechItemAvailabilityService();
|
258
|
+
|
259
|
+
String itemId = "{{ITEM_ID}}";
|
260
|
+
boolean available = {{AVAILABLE_STATUS}}; // true or false
|
261
|
+
|
262
|
+
// Example 1: Single item update
|
263
|
+
service.updateItemAvailability(itemId, available, "Inventory sync");
|
264
|
+
|
265
|
+
// Example 2: Mark item out of stock
|
266
|
+
// service.markOutOfStock("item-123");
|
267
|
+
|
268
|
+
// Example 3: Bulk update from inventory
|
269
|
+
// service.syncInventory(Map.of(
|
270
|
+
// "item-123", false, // Out of stock
|
271
|
+
// "item-456", true, // In stock
|
272
|
+
// "item-789", true // In stock
|
273
|
+
// ));
|
274
|
+
|
275
|
+
} catch (Exception e) {
|
276
|
+
System.exit(1);
|
277
|
+
}
|
278
|
+
}
|
279
|
+
}
|