nexium-storage 0.1.0__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.
- nexium_storage-0.1.0/PKG-INFO +88 -0
- nexium_storage-0.1.0/README.md +64 -0
- nexium_storage-0.1.0/nexium_storage/__init__.py +4 -0
- nexium_storage-0.1.0/nexium_storage/client.py +169 -0
- nexium_storage-0.1.0/nexium_storage.egg-info/PKG-INFO +88 -0
- nexium_storage-0.1.0/nexium_storage.egg-info/SOURCES.txt +9 -0
- nexium_storage-0.1.0/nexium_storage.egg-info/dependency_links.txt +1 -0
- nexium_storage-0.1.0/nexium_storage.egg-info/requires.txt +3 -0
- nexium_storage-0.1.0/nexium_storage.egg-info/top_level.txt +1 -0
- nexium_storage-0.1.0/pyproject.toml +39 -0
- nexium_storage-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nexium-storage
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for NEXIUM Storage
|
|
5
|
+
Author-email: NEXIUM <ai.nexium@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://nexiumai.io
|
|
8
|
+
Project-URL: Documentation, https://console.nexiumai.io/docs
|
|
9
|
+
Keywords: nexium,storage,s3,r2,file-upload,cloud
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Provides-Extra: requests
|
|
23
|
+
Requires-Dist: requests>=2.28; extra == "requests"
|
|
24
|
+
|
|
25
|
+
# nexium-storage
|
|
26
|
+
|
|
27
|
+
Official Python SDK for [NEXIUM Storage](https://nexiumai.io) — upload, serve and manage files via a simple API.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install nexium-storage
|
|
33
|
+
|
|
34
|
+
# For file upload support:
|
|
35
|
+
pip install "nexium-storage[requests]"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import os
|
|
42
|
+
from nexium_storage import NexiumStorage
|
|
43
|
+
|
|
44
|
+
storage = NexiumStorage(api_key=os.getenv("NEXIUM_API_KEY"))
|
|
45
|
+
|
|
46
|
+
# Upload
|
|
47
|
+
with open("photo.jpg", "rb") as f:
|
|
48
|
+
file = storage.upload(bucket_id, f, "photo.jpg", "image/jpeg")
|
|
49
|
+
print(file.url)
|
|
50
|
+
|
|
51
|
+
# List
|
|
52
|
+
result = storage.list(bucket_id, search="photo")
|
|
53
|
+
for f in result.files:
|
|
54
|
+
print(f.filename, f.size_bytes)
|
|
55
|
+
|
|
56
|
+
# Download URL
|
|
57
|
+
url = storage.download(file.id)
|
|
58
|
+
|
|
59
|
+
# Rename
|
|
60
|
+
storage.rename(file.id, "new-name.jpg")
|
|
61
|
+
|
|
62
|
+
# Delete
|
|
63
|
+
storage.delete(file.id)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Webhook verification
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from nexium_storage import NexiumStorage, NexiumError
|
|
70
|
+
|
|
71
|
+
# Flask example
|
|
72
|
+
@app.route("/webhook", methods=["POST"])
|
|
73
|
+
def webhook():
|
|
74
|
+
try:
|
|
75
|
+
payload = NexiumStorage.verify_webhook(
|
|
76
|
+
request.get_data(),
|
|
77
|
+
request.headers.get("X-Nexium-Signature"),
|
|
78
|
+
os.getenv("NEXIUM_WEBHOOK_SECRET"),
|
|
79
|
+
)
|
|
80
|
+
print(payload["event"], payload["data"])
|
|
81
|
+
return "", 200
|
|
82
|
+
except NexiumError:
|
|
83
|
+
return "", 401
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# nexium-storage
|
|
2
|
+
|
|
3
|
+
Official Python SDK for [NEXIUM Storage](https://nexiumai.io) — upload, serve and manage files via a simple API.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install nexium-storage
|
|
9
|
+
|
|
10
|
+
# For file upload support:
|
|
11
|
+
pip install "nexium-storage[requests]"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
import os
|
|
18
|
+
from nexium_storage import NexiumStorage
|
|
19
|
+
|
|
20
|
+
storage = NexiumStorage(api_key=os.getenv("NEXIUM_API_KEY"))
|
|
21
|
+
|
|
22
|
+
# Upload
|
|
23
|
+
with open("photo.jpg", "rb") as f:
|
|
24
|
+
file = storage.upload(bucket_id, f, "photo.jpg", "image/jpeg")
|
|
25
|
+
print(file.url)
|
|
26
|
+
|
|
27
|
+
# List
|
|
28
|
+
result = storage.list(bucket_id, search="photo")
|
|
29
|
+
for f in result.files:
|
|
30
|
+
print(f.filename, f.size_bytes)
|
|
31
|
+
|
|
32
|
+
# Download URL
|
|
33
|
+
url = storage.download(file.id)
|
|
34
|
+
|
|
35
|
+
# Rename
|
|
36
|
+
storage.rename(file.id, "new-name.jpg")
|
|
37
|
+
|
|
38
|
+
# Delete
|
|
39
|
+
storage.delete(file.id)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Webhook verification
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from nexium_storage import NexiumStorage, NexiumError
|
|
46
|
+
|
|
47
|
+
# Flask example
|
|
48
|
+
@app.route("/webhook", methods=["POST"])
|
|
49
|
+
def webhook():
|
|
50
|
+
try:
|
|
51
|
+
payload = NexiumStorage.verify_webhook(
|
|
52
|
+
request.get_data(),
|
|
53
|
+
request.headers.get("X-Nexium-Signature"),
|
|
54
|
+
os.getenv("NEXIUM_WEBHOOK_SECRET"),
|
|
55
|
+
)
|
|
56
|
+
print(payload["event"], payload["data"])
|
|
57
|
+
return "", 200
|
|
58
|
+
except NexiumError:
|
|
59
|
+
return "", 401
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import hmac
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import IO, Any, Callable, Optional
|
|
8
|
+
import urllib.request
|
|
9
|
+
import urllib.parse
|
|
10
|
+
import urllib.error
|
|
11
|
+
|
|
12
|
+
DEFAULT_BASE_URL = "https://api.nexiumai.io"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class StoredFile:
|
|
17
|
+
id: str
|
|
18
|
+
bucket_id: str
|
|
19
|
+
object_key: str
|
|
20
|
+
filename: str
|
|
21
|
+
mime_type: str
|
|
22
|
+
size_bytes: int
|
|
23
|
+
url: str
|
|
24
|
+
created_at: str
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def from_dict(cls, d: dict) -> "StoredFile":
|
|
28
|
+
return cls(**{k: d[k] for k in cls.__dataclass_fields__})
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class FileList:
|
|
33
|
+
files: list[StoredFile]
|
|
34
|
+
total: int
|
|
35
|
+
page: int
|
|
36
|
+
per_page: int
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def from_dict(cls, d: dict) -> "FileList":
|
|
40
|
+
return cls(
|
|
41
|
+
files=[StoredFile.from_dict(f) for f in d.get("files", [])],
|
|
42
|
+
total=d["total"],
|
|
43
|
+
page=d["page"],
|
|
44
|
+
per_page=d["per_page"],
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class NexiumError(Exception):
|
|
49
|
+
def __init__(self, status: int, message: str):
|
|
50
|
+
super().__init__(message)
|
|
51
|
+
self.status = status
|
|
52
|
+
self.message = message
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class NexiumStorage:
|
|
56
|
+
"""NEXIUM Storage Python SDK."""
|
|
57
|
+
|
|
58
|
+
def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL):
|
|
59
|
+
if not api_key:
|
|
60
|
+
raise ValueError("api_key is required")
|
|
61
|
+
self.api_key = api_key
|
|
62
|
+
self.base_url = base_url.rstrip("/")
|
|
63
|
+
|
|
64
|
+
def _request(self, method: str, path: str, **kwargs) -> Any:
|
|
65
|
+
try:
|
|
66
|
+
import requests
|
|
67
|
+
json_body = kwargs.pop("json_body", None)
|
|
68
|
+
headers = {"Authorization": f"Bearer {self.api_key}"}
|
|
69
|
+
req_kwargs: dict = {"headers": headers, **kwargs}
|
|
70
|
+
if json_body is not None:
|
|
71
|
+
req_kwargs["json"] = json_body
|
|
72
|
+
resp = requests.request(method, f"{self.base_url}{path}", **req_kwargs)
|
|
73
|
+
if resp.status_code == 204:
|
|
74
|
+
return None
|
|
75
|
+
data = resp.json()
|
|
76
|
+
if not resp.ok:
|
|
77
|
+
raise NexiumError(resp.status_code, data.get("message", "Request failed"))
|
|
78
|
+
return data
|
|
79
|
+
except ImportError:
|
|
80
|
+
return self._request_stdlib(method, path, **kwargs)
|
|
81
|
+
|
|
82
|
+
def _request_stdlib(self, method: str, path: str, json_body=None, **_) -> Any:
|
|
83
|
+
url = f"{self.base_url}{path}"
|
|
84
|
+
body = json.dumps(json_body).encode() if json_body else None
|
|
85
|
+
headers = {
|
|
86
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
87
|
+
"Content-Type": "application/json",
|
|
88
|
+
}
|
|
89
|
+
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
|
90
|
+
try:
|
|
91
|
+
with urllib.request.urlopen(req) as resp:
|
|
92
|
+
if resp.status == 204:
|
|
93
|
+
return None
|
|
94
|
+
return json.loads(resp.read())
|
|
95
|
+
except urllib.error.HTTPError as e:
|
|
96
|
+
data = json.loads(e.read())
|
|
97
|
+
raise NexiumError(e.code, data.get("message", "Request failed")) from e
|
|
98
|
+
|
|
99
|
+
# ─── Files ────────────────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
def list(
|
|
102
|
+
self,
|
|
103
|
+
bucket_id: str,
|
|
104
|
+
search: str = "",
|
|
105
|
+
page: int = 1,
|
|
106
|
+
per_page: int = 24,
|
|
107
|
+
) -> FileList:
|
|
108
|
+
"""List files in a bucket."""
|
|
109
|
+
params: dict[str, Any] = {"page": page, "per_page": per_page}
|
|
110
|
+
if search:
|
|
111
|
+
params["search"] = search
|
|
112
|
+
qs = urllib.parse.urlencode(params)
|
|
113
|
+
data = self._request("GET", f"/api/v1/ext/buckets/{bucket_id}/files?{qs}")
|
|
114
|
+
return FileList.from_dict(data)
|
|
115
|
+
|
|
116
|
+
def upload(
|
|
117
|
+
self,
|
|
118
|
+
bucket_id: str,
|
|
119
|
+
file: IO[bytes],
|
|
120
|
+
filename: str,
|
|
121
|
+
mime_type: str = "application/octet-stream",
|
|
122
|
+
on_progress: Optional[Callable[[int], None]] = None,
|
|
123
|
+
) -> StoredFile:
|
|
124
|
+
"""Upload a file to a bucket. `file` can be any file-like object."""
|
|
125
|
+
try:
|
|
126
|
+
import requests
|
|
127
|
+
headers = {"Authorization": f"Bearer {self.api_key}"}
|
|
128
|
+
resp = requests.post(
|
|
129
|
+
f"{self.base_url}/api/v1/ext/buckets/{bucket_id}/files",
|
|
130
|
+
headers=headers,
|
|
131
|
+
files={"file": (filename, file, mime_type)},
|
|
132
|
+
)
|
|
133
|
+
data = resp.json()
|
|
134
|
+
if not resp.ok:
|
|
135
|
+
raise NexiumError(resp.status_code, data.get("message", "Upload failed"))
|
|
136
|
+
return StoredFile.from_dict(data)
|
|
137
|
+
except ImportError:
|
|
138
|
+
raise NexiumError(0, "Install `requests` for file upload: pip install requests")
|
|
139
|
+
|
|
140
|
+
def download(self, file_id: str) -> str:
|
|
141
|
+
"""Get a temporary download URL for a file."""
|
|
142
|
+
data = self._request("GET", f"/api/v1/ext/files/{file_id}/download")
|
|
143
|
+
return data["url"]
|
|
144
|
+
|
|
145
|
+
def rename(self, file_id: str, filename: str) -> StoredFile:
|
|
146
|
+
"""Rename a file."""
|
|
147
|
+
data = self._request("PATCH", f"/api/v1/ext/files/{file_id}", json_body={"filename": filename})
|
|
148
|
+
return StoredFile.from_dict(data)
|
|
149
|
+
|
|
150
|
+
def delete(self, file_id: str) -> None:
|
|
151
|
+
"""Delete a file."""
|
|
152
|
+
self._request("DELETE", f"/api/v1/ext/files/{file_id}")
|
|
153
|
+
|
|
154
|
+
# ─── Webhooks ─────────────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def verify_webhook(raw_body: bytes | str, signature: str, secret: str) -> Any:
|
|
158
|
+
"""Verify a webhook signature and return the parsed payload.
|
|
159
|
+
|
|
160
|
+
Raises NexiumError if the signature is invalid.
|
|
161
|
+
"""
|
|
162
|
+
if isinstance(raw_body, str):
|
|
163
|
+
raw_body = raw_body.encode()
|
|
164
|
+
expected = "sha256=" + hmac.new(
|
|
165
|
+
secret.encode(), raw_body, hashlib.sha256
|
|
166
|
+
).hexdigest()
|
|
167
|
+
if not hmac.compare_digest(expected, signature):
|
|
168
|
+
raise NexiumError(401, "Invalid webhook signature")
|
|
169
|
+
return json.loads(raw_body)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nexium-storage
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for NEXIUM Storage
|
|
5
|
+
Author-email: NEXIUM <ai.nexium@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://nexiumai.io
|
|
8
|
+
Project-URL: Documentation, https://console.nexiumai.io/docs
|
|
9
|
+
Keywords: nexium,storage,s3,r2,file-upload,cloud
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
Provides-Extra: requests
|
|
23
|
+
Requires-Dist: requests>=2.28; extra == "requests"
|
|
24
|
+
|
|
25
|
+
# nexium-storage
|
|
26
|
+
|
|
27
|
+
Official Python SDK for [NEXIUM Storage](https://nexiumai.io) — upload, serve and manage files via a simple API.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install nexium-storage
|
|
33
|
+
|
|
34
|
+
# For file upload support:
|
|
35
|
+
pip install "nexium-storage[requests]"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import os
|
|
42
|
+
from nexium_storage import NexiumStorage
|
|
43
|
+
|
|
44
|
+
storage = NexiumStorage(api_key=os.getenv("NEXIUM_API_KEY"))
|
|
45
|
+
|
|
46
|
+
# Upload
|
|
47
|
+
with open("photo.jpg", "rb") as f:
|
|
48
|
+
file = storage.upload(bucket_id, f, "photo.jpg", "image/jpeg")
|
|
49
|
+
print(file.url)
|
|
50
|
+
|
|
51
|
+
# List
|
|
52
|
+
result = storage.list(bucket_id, search="photo")
|
|
53
|
+
for f in result.files:
|
|
54
|
+
print(f.filename, f.size_bytes)
|
|
55
|
+
|
|
56
|
+
# Download URL
|
|
57
|
+
url = storage.download(file.id)
|
|
58
|
+
|
|
59
|
+
# Rename
|
|
60
|
+
storage.rename(file.id, "new-name.jpg")
|
|
61
|
+
|
|
62
|
+
# Delete
|
|
63
|
+
storage.delete(file.id)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Webhook verification
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from nexium_storage import NexiumStorage, NexiumError
|
|
70
|
+
|
|
71
|
+
# Flask example
|
|
72
|
+
@app.route("/webhook", methods=["POST"])
|
|
73
|
+
def webhook():
|
|
74
|
+
try:
|
|
75
|
+
payload = NexiumStorage.verify_webhook(
|
|
76
|
+
request.get_data(),
|
|
77
|
+
request.headers.get("X-Nexium-Signature"),
|
|
78
|
+
os.getenv("NEXIUM_WEBHOOK_SECRET"),
|
|
79
|
+
)
|
|
80
|
+
print(payload["event"], payload["data"])
|
|
81
|
+
return "", 200
|
|
82
|
+
except NexiumError:
|
|
83
|
+
return "", 401
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
nexium_storage/__init__.py
|
|
4
|
+
nexium_storage/client.py
|
|
5
|
+
nexium_storage.egg-info/PKG-INFO
|
|
6
|
+
nexium_storage.egg-info/SOURCES.txt
|
|
7
|
+
nexium_storage.egg-info/dependency_links.txt
|
|
8
|
+
nexium_storage.egg-info/requires.txt
|
|
9
|
+
nexium_storage.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nexium_storage
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nexium-storage"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python SDK for NEXIUM Storage"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "NEXIUM", email = "ai.nexium@gmail.com" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["nexium", "storage", "s3", "r2", "file-upload", "cloud"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.8",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
26
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
|
+
]
|
|
28
|
+
dependencies = []
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
requests = ["requests>=2.28"]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://nexiumai.io"
|
|
35
|
+
Documentation = "https://console.nexiumai.io/docs"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["."]
|
|
39
|
+
include = ["nexium_storage*"]
|