lumera 0.27.1__tar.gz → 0.28.0.dev2__tar.gz

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 (37) hide show
  1. {lumera-0.27.1 → lumera-0.28.0.dev2}/PKG-INFO +1 -1
  2. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/integrations/netsuite.py +40 -3
  3. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera.egg-info/PKG-INFO +1 -1
  4. {lumera-0.27.1 → lumera-0.28.0.dev2}/pyproject.toml +1 -1
  5. {lumera-0.27.1 → lumera-0.28.0.dev2}/tests/test_integrations_netsuite.py +27 -0
  6. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/__init__.py +0 -0
  7. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/_live.py +0 -0
  8. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/_utils.py +0 -0
  9. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/agents.py +0 -0
  10. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/automations.py +0 -0
  11. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/email.py +0 -0
  12. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/exceptions.py +0 -0
  13. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/files.py +0 -0
  14. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/flags.py +0 -0
  15. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/google.py +0 -0
  16. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/integrations/__init__.py +0 -0
  17. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/integrations/bill.py +0 -0
  18. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/integrations/google.py +0 -0
  19. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/integrations/salesforce.py +0 -0
  20. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/integrations/slack.py +0 -0
  21. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/llm.py +0 -0
  22. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/pb.py +0 -0
  23. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/sdk.py +0 -0
  24. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/storage.py +0 -0
  25. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera/webhooks.py +0 -0
  26. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera.egg-info/SOURCES.txt +0 -0
  27. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera.egg-info/dependency_links.txt +0 -0
  28. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera.egg-info/requires.txt +0 -0
  29. {lumera-0.27.1 → lumera-0.28.0.dev2}/lumera.egg-info/top_level.txt +0 -0
  30. {lumera-0.27.1 → lumera-0.28.0.dev2}/setup.cfg +0 -0
  31. {lumera-0.27.1 → lumera-0.28.0.dev2}/tests/test_agents_live.py +0 -0
  32. {lumera-0.27.1 → lumera-0.28.0.dev2}/tests/test_automations.py +0 -0
  33. {lumera-0.27.1 → lumera-0.28.0.dev2}/tests/test_flags.py +0 -0
  34. {lumera-0.27.1 → lumera-0.28.0.dev2}/tests/test_integrations_salesforce.py +0 -0
  35. {lumera-0.27.1 → lumera-0.28.0.dev2}/tests/test_lazy_imports.py +0 -0
  36. {lumera-0.27.1 → lumera-0.28.0.dev2}/tests/test_sdk.py +0 -0
  37. {lumera-0.27.1 → lumera-0.28.0.dev2}/tests/test_webhooks.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lumera
3
- Version: 0.27.1
3
+ Version: 0.28.0.dev2
4
4
  Summary: SDK for building on Lumera platform
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: requests
@@ -3,9 +3,9 @@ Lumera NetSuite Integration
3
3
 
4
4
  Provides two NetSuite clients:
5
5
 
6
- * :class:`NetSuiteProxyClient` is recommended for new code. It routes requests
7
- through Lumera's integration proxy, so NetSuite credentials stay inside the
8
- Lumera backend.
6
+ * :class:`NetSuiteProxyClient` is recommended for new code. It routes SuiteQL,
7
+ REST records, and RESTlets through Lumera's integration proxy, so NetSuite
8
+ credentials stay inside the Lumera backend.
9
9
  * :class:`NetSuiteClient` preserves the existing direct-call behavior for
10
10
  backward compatibility. It downloads the NetSuite access token into the
11
11
  automation process and calls SuiteTalk directly.
@@ -99,6 +99,10 @@ def _base_url(account_id: str) -> str:
99
99
  return f"https://{account_id.lower()}.suitetalk.api.netsuite.com"
100
100
 
101
101
 
102
+ def _restlet_base_url(account_id: str) -> str:
103
+ return f"https://{account_id.lower()}.restlets.api.netsuite.com"
104
+
105
+
102
106
  def _fetch_account_id(api_name: str) -> str:
103
107
  """Resolve the NetSuite account ID from connection metadata (no secrets)."""
104
108
  cached = _account_id_cache.get(api_name)
@@ -417,6 +421,39 @@ class NetSuiteProxyClient:
417
421
  timeout=self._timeout,
418
422
  )
419
423
 
424
+ def call_restlet(
425
+ self,
426
+ script_id: str | int,
427
+ deployment_id: str | int,
428
+ *,
429
+ method: str = "POST",
430
+ body: dict[str, Any] | list[Any] | str | None = None,
431
+ headers: dict[str, str] | None = None,
432
+ params: dict[str, Any] | None = None,
433
+ ) -> requests.Response:
434
+ """Call a deployed RESTlet through Lumera's integration proxy.
435
+
436
+ RESTlet access must first be enabled for the NetSuite connection in
437
+ Lumera. The raw response is returned so callers can handle each
438
+ RESTlet's custom response format.
439
+ """
440
+ query: dict[str, Any] = {
441
+ "script": script_id,
442
+ "deploy": deployment_id,
443
+ }
444
+ for key, value in (params or {}).items():
445
+ if key not in query:
446
+ query[key] = value
447
+ url = f"{_restlet_base_url(self.account_id)}/app/site/hosting/restlet.nl?{urlencode(query)}"
448
+ return _proxy_request(
449
+ self._api_name,
450
+ method.upper(),
451
+ url,
452
+ body=body,
453
+ headers=headers,
454
+ timeout=self._timeout,
455
+ )
456
+
420
457
  # ── SuiteQL ──────────────────────────────────────────────────────────
421
458
 
422
459
  def suiteql(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lumera
3
- Version: 0.27.1
3
+ Version: 0.28.0.dev2
4
4
  Summary: SDK for building on Lumera platform
5
5
  Requires-Python: >=3.11
6
6
  Requires-Dist: requests
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lumera"
3
- version = "0.27.1"
3
+ version = "0.28.0.dev2"
4
4
  description = "SDK for building on Lumera platform"
5
5
  requires-python = ">=3.11"
6
6
  dependencies = [
@@ -177,6 +177,33 @@ def test_proxy_client_request_builds_url_with_params(monkeypatch: pytest.MonkeyP
177
177
  assert "body" not in payload
178
178
 
179
179
 
180
+ def test_proxy_client_call_restlet_uses_restlet_host(
181
+ monkeypatch: pytest.MonkeyPatch,
182
+ ) -> None:
183
+ session = MockSession(proxy_responses=[DummyResponse(json_data={"fileId": "42"})])
184
+ _install(monkeypatch, session)
185
+
186
+ ns = netsuite.NetSuiteProxyClient(api_name="netsuite_prod", timeout=120)
187
+ resp = ns.call_restlet(
188
+ 990,
189
+ 1,
190
+ body={"journalEntryId": "123"},
191
+ params={"mode": "attach"},
192
+ )
193
+
194
+ assert resp.json() == {"fileId": "42"}
195
+ call = session.post_calls[0]
196
+ payload = call["json"]
197
+ assert payload["api_name"] == "netsuite_prod"
198
+ assert payload["method"] == "POST"
199
+ assert payload["url"] == (
200
+ "https://12345_sb1.restlets.api.netsuite.com"
201
+ "/app/site/hosting/restlet.nl?script=990&deploy=1&mode=attach"
202
+ )
203
+ assert payload["body"] == {"journalEntryId": "123"}
204
+ assert payload["timeout_seconds"] == 120
205
+
206
+
180
207
  def test_proxy_timeout_is_bounded_and_requires_positive_value(
181
208
  monkeypatch: pytest.MonkeyPatch,
182
209
  ) -> None:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes