almanac-contracts 0.5.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.
- almanac_contracts-0.5.0/PKG-INFO +6 -0
- almanac_contracts-0.5.0/pyproject.toml +14 -0
- almanac_contracts-0.5.0/setup.cfg +4 -0
- almanac_contracts-0.5.0/src/almanac_contracts/__init__.py +1 -0
- almanac_contracts-0.5.0/src/almanac_contracts/errors.py +65 -0
- almanac_contracts-0.5.0/src/almanac_contracts/hosted/__init__.py +77 -0
- almanac_contracts-0.5.0/src/almanac_contracts/hosted/api_keys.py +18 -0
- almanac_contracts-0.5.0/src/almanac_contracts/hosted/billing.py +71 -0
- almanac_contracts-0.5.0/src/almanac_contracts/hosted/jobs.py +63 -0
- almanac_contracts-0.5.0/src/almanac_contracts/hosted/pages.py +43 -0
- almanac_contracts-0.5.0/src/almanac_contracts/hosted/sources.py +82 -0
- almanac_contracts-0.5.0/src/almanac_contracts/hosted/status.py +10 -0
- almanac_contracts-0.5.0/src/almanac_contracts/hosted/topics.py +12 -0
- almanac_contracts-0.5.0/src/almanac_contracts/hosted/wikis.py +9 -0
- almanac_contracts-0.5.0/src/almanac_contracts/hosted/workspaces.py +63 -0
- almanac_contracts-0.5.0/src/almanac_contracts/models.py +10 -0
- almanac_contracts-0.5.0/src/almanac_contracts/urls.py +16 -0
- almanac_contracts-0.5.0/src/almanac_contracts.egg-info/PKG-INFO +6 -0
- almanac_contracts-0.5.0/src/almanac_contracts.egg-info/SOURCES.txt +20 -0
- almanac_contracts-0.5.0/src/almanac_contracts.egg-info/dependency_links.txt +1 -0
- almanac_contracts-0.5.0/src/almanac_contracts.egg-info/requires.txt +1 -0
- almanac_contracts-0.5.0/src/almanac_contracts.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "almanac-contracts"
|
|
3
|
+
version = "0.5.0"
|
|
4
|
+
description = "Shared wire contracts for Almanac clients and services."
|
|
5
|
+
requires-python = ">=3.12"
|
|
6
|
+
dependencies = ["pydantic>=2.8"]
|
|
7
|
+
|
|
8
|
+
[build-system]
|
|
9
|
+
requires = ["setuptools>=77"]
|
|
10
|
+
build-backend = "setuptools.build_meta"
|
|
11
|
+
|
|
12
|
+
[tool.setuptools.packages.find]
|
|
13
|
+
where = ["src"]
|
|
14
|
+
include = ["almanac_contracts*"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Stable contracts shared by Almanac's public edges."""
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
ErrorCode = Literal[
|
|
4
|
+
"almanac_error",
|
|
5
|
+
"not_found",
|
|
6
|
+
"conflict",
|
|
7
|
+
"not_authenticated",
|
|
8
|
+
"forbidden",
|
|
9
|
+
"validation_failed",
|
|
10
|
+
"provider_unavailable",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AlmanacError(Exception):
|
|
15
|
+
"""Base class for product errors that can cross CLI and server edges."""
|
|
16
|
+
|
|
17
|
+
code: ErrorCode = "almanac_error"
|
|
18
|
+
|
|
19
|
+
def __init__(self, message: str, *, request_id: str | None = None):
|
|
20
|
+
super().__init__(message)
|
|
21
|
+
self.request_id = request_id
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class NotFoundError(AlmanacError):
|
|
25
|
+
code: ErrorCode = "not_found"
|
|
26
|
+
|
|
27
|
+
def __init__(self, resource: str, identifier: str):
|
|
28
|
+
super().__init__(f"{resource} not found: {identifier}")
|
|
29
|
+
self.resource = resource
|
|
30
|
+
self.identifier = identifier
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ConflictError(AlmanacError):
|
|
34
|
+
code: ErrorCode = "conflict"
|
|
35
|
+
|
|
36
|
+
def __init__(self, message: str):
|
|
37
|
+
super().__init__(message)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ValidationFailed(AlmanacError):
|
|
41
|
+
code: ErrorCode = "validation_failed"
|
|
42
|
+
|
|
43
|
+
def __init__(self, message: str):
|
|
44
|
+
super().__init__(message)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class NotAuthenticated(AlmanacError):
|
|
48
|
+
code: ErrorCode = "not_authenticated"
|
|
49
|
+
|
|
50
|
+
def __init__(self, message: str = "Missing or invalid credentials"):
|
|
51
|
+
super().__init__(message)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ForbiddenError(AlmanacError):
|
|
55
|
+
code: ErrorCode = "forbidden"
|
|
56
|
+
|
|
57
|
+
def __init__(self, message: str = "Insufficient permission"):
|
|
58
|
+
super().__init__(message)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ProviderUnavailable(AlmanacError):
|
|
62
|
+
code: ErrorCode = "provider_unavailable"
|
|
63
|
+
|
|
64
|
+
def __init__(self, message: str):
|
|
65
|
+
super().__init__(message)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from almanac_contracts.hosted.api_keys import ApiKey, CreatedApiKey
|
|
2
|
+
from almanac_contracts.hosted.billing import (
|
|
3
|
+
BillingPlanChange,
|
|
4
|
+
BillingPortal,
|
|
5
|
+
BillingPreview,
|
|
6
|
+
BillingState,
|
|
7
|
+
)
|
|
8
|
+
from almanac_contracts.hosted.jobs import (
|
|
9
|
+
Job,
|
|
10
|
+
JobEvent,
|
|
11
|
+
JobImportOutcome,
|
|
12
|
+
JobOutcome,
|
|
13
|
+
JobPageChange,
|
|
14
|
+
JobSourceChange,
|
|
15
|
+
)
|
|
16
|
+
from almanac_contracts.hosted.pages import (
|
|
17
|
+
Page,
|
|
18
|
+
PageRecord,
|
|
19
|
+
PageSearchResult,
|
|
20
|
+
PageSource,
|
|
21
|
+
PageSummary,
|
|
22
|
+
)
|
|
23
|
+
from almanac_contracts.hosted.sources import (
|
|
24
|
+
ImportItem,
|
|
25
|
+
Source,
|
|
26
|
+
SourceFolder,
|
|
27
|
+
SourceTree,
|
|
28
|
+
Upload,
|
|
29
|
+
UploadFilePlan,
|
|
30
|
+
UploadPlan,
|
|
31
|
+
UploadResult,
|
|
32
|
+
UploadTarget,
|
|
33
|
+
)
|
|
34
|
+
from almanac_contracts.hosted.topics import Topic, TopicSummary
|
|
35
|
+
from almanac_contracts.hosted.wikis import Wiki
|
|
36
|
+
from almanac_contracts.hosted.workspaces import (
|
|
37
|
+
Workspace,
|
|
38
|
+
WorkspaceInvitation,
|
|
39
|
+
WorkspaceMember,
|
|
40
|
+
WorkspaceMembers,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
"ApiKey",
|
|
45
|
+
"BillingPlanChange",
|
|
46
|
+
"BillingPortal",
|
|
47
|
+
"BillingPreview",
|
|
48
|
+
"BillingState",
|
|
49
|
+
"CreatedApiKey",
|
|
50
|
+
"Job",
|
|
51
|
+
"JobEvent",
|
|
52
|
+
"JobImportOutcome",
|
|
53
|
+
"JobOutcome",
|
|
54
|
+
"JobPageChange",
|
|
55
|
+
"JobSourceChange",
|
|
56
|
+
"ImportItem",
|
|
57
|
+
"Page",
|
|
58
|
+
"PageRecord",
|
|
59
|
+
"PageSearchResult",
|
|
60
|
+
"PageSource",
|
|
61
|
+
"PageSummary",
|
|
62
|
+
"Source",
|
|
63
|
+
"SourceFolder",
|
|
64
|
+
"SourceTree",
|
|
65
|
+
"Topic",
|
|
66
|
+
"TopicSummary",
|
|
67
|
+
"Upload",
|
|
68
|
+
"UploadFilePlan",
|
|
69
|
+
"UploadPlan",
|
|
70
|
+
"UploadResult",
|
|
71
|
+
"UploadTarget",
|
|
72
|
+
"Wiki",
|
|
73
|
+
"Workspace",
|
|
74
|
+
"WorkspaceInvitation",
|
|
75
|
+
"WorkspaceMember",
|
|
76
|
+
"WorkspaceMembers",
|
|
77
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
|
|
3
|
+
from almanac_contracts.models import AlmanacModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ApiKey(AlmanacModel):
|
|
7
|
+
api_key_id: str
|
|
8
|
+
name: str
|
|
9
|
+
obfuscated_value: str
|
|
10
|
+
permissions: list[str] = Field(default_factory=list)
|
|
11
|
+
created_at: str
|
|
12
|
+
updated_at: str
|
|
13
|
+
last_used_at: str | None = None
|
|
14
|
+
expires_at: str | None = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CreatedApiKey(ApiKey):
|
|
18
|
+
value: str
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from almanac_contracts.models import AlmanacModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PlanFeature(AlmanacModel):
|
|
9
|
+
feature_id: str
|
|
10
|
+
included: float | None = None
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BillingUsage(AlmanacModel):
|
|
14
|
+
feature_id: str
|
|
15
|
+
granted: float
|
|
16
|
+
remaining: float
|
|
17
|
+
usage: float
|
|
18
|
+
unlimited: bool = False
|
|
19
|
+
overage_allowed: bool = False
|
|
20
|
+
next_reset_at: float | None = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BillingPlan(AlmanacModel):
|
|
24
|
+
plan_id: str
|
|
25
|
+
name: str
|
|
26
|
+
group: str | None = None
|
|
27
|
+
price: float | None = None
|
|
28
|
+
interval: str | None = None
|
|
29
|
+
action: (
|
|
30
|
+
Literal["activate", "upgrade", "downgrade", "purchase", "none"] | None
|
|
31
|
+
) = None
|
|
32
|
+
status: Literal["active", "scheduled"] | None = None
|
|
33
|
+
features: list[PlanFeature] = Field(default_factory=list)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class BillingManage(AlmanacModel):
|
|
37
|
+
allowed: bool
|
|
38
|
+
reason: Literal["permission", "not_allowed"]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class BillingState(AlmanacModel):
|
|
42
|
+
workspace_id: str
|
|
43
|
+
manage: BillingManage
|
|
44
|
+
plans: list[BillingPlan]
|
|
45
|
+
usage: list[BillingUsage] = Field(default_factory=list)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class BillingPortal(AlmanacModel):
|
|
49
|
+
url: str
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class BillingPlanChange(AlmanacModel):
|
|
53
|
+
redirect_url: str | None = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class BillingPreviewLine(AlmanacModel):
|
|
57
|
+
name: str
|
|
58
|
+
description: str | None = None
|
|
59
|
+
subtotal: float | None = None
|
|
60
|
+
total: float | None = None
|
|
61
|
+
quantity: float | None = None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class BillingPreview(AlmanacModel):
|
|
65
|
+
plan_id: str
|
|
66
|
+
currency: str | None = None
|
|
67
|
+
subtotal: float | None = None
|
|
68
|
+
total: float | None = None
|
|
69
|
+
redirect_to_checkout: bool = False
|
|
70
|
+
checkout_type: str | None = None
|
|
71
|
+
line_items: list[BillingPreviewLine] = Field(default_factory=list)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from almanac_contracts.hosted.status import (
|
|
4
|
+
ImportStatus,
|
|
5
|
+
JobEventLevel,
|
|
6
|
+
JobKind,
|
|
7
|
+
JobStatus,
|
|
8
|
+
JobStatusState,
|
|
9
|
+
)
|
|
10
|
+
from almanac_contracts.models import AlmanacModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Job(AlmanacModel):
|
|
14
|
+
job_id: str
|
|
15
|
+
wiki_id: str
|
|
16
|
+
kind: JobKind
|
|
17
|
+
status: JobStatus
|
|
18
|
+
title: str | None
|
|
19
|
+
current_step: str | None
|
|
20
|
+
summary: str | None
|
|
21
|
+
error: str | None
|
|
22
|
+
status_state: JobStatusState
|
|
23
|
+
status_label: str
|
|
24
|
+
created_at: str
|
|
25
|
+
updated_at: str
|
|
26
|
+
started_at: str | None
|
|
27
|
+
finished_at: str | None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class JobEvent(AlmanacModel):
|
|
31
|
+
event_id: str
|
|
32
|
+
job_id: str
|
|
33
|
+
sequence: int
|
|
34
|
+
level: JobEventLevel
|
|
35
|
+
message: str
|
|
36
|
+
created_at: str
|
|
37
|
+
payload_json: dict[str, object] | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class JobImportOutcome(AlmanacModel):
|
|
41
|
+
original_path: str
|
|
42
|
+
status: ImportStatus
|
|
43
|
+
source_path: str | None
|
|
44
|
+
error: str | None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class JobSourceChange(AlmanacModel):
|
|
48
|
+
action: Literal["added", "moved"]
|
|
49
|
+
path: str
|
|
50
|
+
previous_path: str | None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class JobPageChange(AlmanacModel):
|
|
54
|
+
action: Literal["created", "updated", "removed"]
|
|
55
|
+
path: str
|
|
56
|
+
title: str
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class JobOutcome(AlmanacModel):
|
|
60
|
+
job: Job
|
|
61
|
+
imports: list[JobImportOutcome]
|
|
62
|
+
sources: list[JobSourceChange]
|
|
63
|
+
pages: list[JobPageChange]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from almanac_contracts.hosted.status import PageStatus, StatusState
|
|
2
|
+
from almanac_contracts.models import AlmanacModel
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class PageRecord(AlmanacModel):
|
|
6
|
+
page_id: str
|
|
7
|
+
wiki_id: str
|
|
8
|
+
path: str
|
|
9
|
+
title: str
|
|
10
|
+
status: PageStatus
|
|
11
|
+
updated_at: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PageSummary(PageRecord):
|
|
15
|
+
folder: str
|
|
16
|
+
name: str
|
|
17
|
+
status_state: StatusState
|
|
18
|
+
status_label: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PageSearchResult(PageSummary):
|
|
22
|
+
summary: str
|
|
23
|
+
rank: float
|
|
24
|
+
excerpt: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Page(PageSummary):
|
|
28
|
+
file_path: str
|
|
29
|
+
summary: str
|
|
30
|
+
body_md: str
|
|
31
|
+
frontmatter_json: dict[str, object]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class PageSource(AlmanacModel):
|
|
35
|
+
page_id: str
|
|
36
|
+
source_id: str
|
|
37
|
+
key: str
|
|
38
|
+
title: str
|
|
39
|
+
job_id: str | None
|
|
40
|
+
note: str
|
|
41
|
+
path: str
|
|
42
|
+
original_filename: str
|
|
43
|
+
created_at: str
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from almanac_contracts.hosted.jobs import Job
|
|
2
|
+
from almanac_contracts.hosted.status import ImportStatus, StatusState, UploadStatus
|
|
3
|
+
from almanac_contracts.models import AlmanacModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Source(AlmanacModel):
|
|
7
|
+
source_id: str
|
|
8
|
+
wiki_id: str
|
|
9
|
+
path: str
|
|
10
|
+
folder: str
|
|
11
|
+
name: str
|
|
12
|
+
original_filename: str
|
|
13
|
+
mime_type: str | None
|
|
14
|
+
size_bytes: int
|
|
15
|
+
sha256: str
|
|
16
|
+
created_at: str
|
|
17
|
+
updated_at: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Upload(AlmanacModel):
|
|
21
|
+
upload_id: str
|
|
22
|
+
wiki_id: str
|
|
23
|
+
status: UploadStatus
|
|
24
|
+
status_state: StatusState
|
|
25
|
+
status_label: str
|
|
26
|
+
source_count: int
|
|
27
|
+
summary: str | None
|
|
28
|
+
error: str | None
|
|
29
|
+
created_at: str
|
|
30
|
+
updated_at: str
|
|
31
|
+
finished_at: str | None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ImportItem(AlmanacModel):
|
|
35
|
+
import_id: str
|
|
36
|
+
original_path: str
|
|
37
|
+
name: str
|
|
38
|
+
status: ImportStatus
|
|
39
|
+
status_state: StatusState
|
|
40
|
+
status_label: str
|
|
41
|
+
source_id: str | None
|
|
42
|
+
duplicate_source_id: str | None
|
|
43
|
+
error: str | None
|
|
44
|
+
mime_type: str | None
|
|
45
|
+
size_bytes: int
|
|
46
|
+
sha256: str
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class SourceFolder(AlmanacModel):
|
|
50
|
+
path: str
|
|
51
|
+
name: str
|
|
52
|
+
parent: str | None
|
|
53
|
+
source_count: int
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class SourceTree(AlmanacModel):
|
|
57
|
+
folders: list[SourceFolder]
|
|
58
|
+
sources: list[Source]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class UploadResult(AlmanacModel):
|
|
62
|
+
upload: Upload
|
|
63
|
+
imports: list[ImportItem]
|
|
64
|
+
job: Job | None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class UploadTarget(AlmanacModel):
|
|
68
|
+
method: str
|
|
69
|
+
url: str
|
|
70
|
+
headers: dict[str, str]
|
|
71
|
+
expires_at: str
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class UploadFilePlan(AlmanacModel):
|
|
75
|
+
file_id: str
|
|
76
|
+
path: str
|
|
77
|
+
target: UploadTarget | None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class UploadPlan(AlmanacModel):
|
|
81
|
+
upload: Upload
|
|
82
|
+
files: list[UploadFilePlan]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
JobKind = Literal["ingest", "garden"]
|
|
4
|
+
JobStatus = Literal["queued", "running", "succeeded", "failed"]
|
|
5
|
+
JobEventLevel = Literal["info", "warning", "error"]
|
|
6
|
+
ImportStatus = Literal["pending", "organized", "duplicate", "failed"]
|
|
7
|
+
PageStatus = Literal["active", "archived"]
|
|
8
|
+
UploadStatus = Literal["uploading", "queued", "completed", "failed"]
|
|
9
|
+
StatusState = Literal["ready", "running", "failed", "unknown"]
|
|
10
|
+
JobStatusState = Literal["queued", "running", "succeeded", "failed", "unknown"]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
|
|
3
|
+
from almanac_contracts.models import AlmanacModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Workspace(AlmanacModel):
|
|
7
|
+
workspace_id: str
|
|
8
|
+
name: str
|
|
9
|
+
slug: str
|
|
10
|
+
role: str
|
|
11
|
+
permissions: list[str] = Field(default_factory=list)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class WorkspaceAccess(AlmanacModel):
|
|
15
|
+
workspace_id: str
|
|
16
|
+
role: str
|
|
17
|
+
can_manage_members: bool = False
|
|
18
|
+
can_manage_billing: bool = False
|
|
19
|
+
can_manage_developers: bool = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class WorkspaceMember(AlmanacModel):
|
|
23
|
+
membership_id: str
|
|
24
|
+
user_id: str
|
|
25
|
+
email: str
|
|
26
|
+
display_name: str
|
|
27
|
+
avatar_url: str = ""
|
|
28
|
+
role: str
|
|
29
|
+
status: str = "active"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class WorkspaceInvitation(AlmanacModel):
|
|
33
|
+
invitation_id: str
|
|
34
|
+
email: str
|
|
35
|
+
role: str
|
|
36
|
+
state: str = "pending"
|
|
37
|
+
expires_at: str | None = None
|
|
38
|
+
created_at: str | None = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class WorkspaceRole(AlmanacModel):
|
|
42
|
+
slug: str
|
|
43
|
+
name: str
|
|
44
|
+
description: str = ""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class WorkspaceSeats(AlmanacModel):
|
|
48
|
+
active: int
|
|
49
|
+
pending: int = 0
|
|
50
|
+
used: int
|
|
51
|
+
granted: float | None = None
|
|
52
|
+
unlimited: bool = False
|
|
53
|
+
overage_allowed: bool = False
|
|
54
|
+
blocked: bool = False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class WorkspaceMembers(AlmanacModel):
|
|
58
|
+
workspace_id: str
|
|
59
|
+
viewer: WorkspaceAccess
|
|
60
|
+
members: list[WorkspaceMember] = Field(default_factory=list)
|
|
61
|
+
invitations: list[WorkspaceInvitation] = Field(default_factory=list)
|
|
62
|
+
roles: list[WorkspaceRole] = Field(default_factory=list)
|
|
63
|
+
seats: WorkspaceSeats
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AlmanacModel(BaseModel):
|
|
5
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PageCursor(AlmanacModel):
|
|
9
|
+
limit: int = Field(default=50, ge=1, le=500)
|
|
10
|
+
offset: int = Field(default=0, ge=0)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from almanac_contracts.errors import ValidationFailed
|
|
4
|
+
|
|
5
|
+
HOSTNAME = re.compile(
|
|
6
|
+
r"(?=.{1,253}\Z)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*"
|
|
7
|
+
r"[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?",
|
|
8
|
+
re.IGNORECASE,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def required_hostname(value: str, name: str) -> str:
|
|
13
|
+
hostname = value.strip()
|
|
14
|
+
if not HOSTNAME.fullmatch(hostname):
|
|
15
|
+
raise ValidationFailed(f"{name} must be a hostname")
|
|
16
|
+
return hostname.lower()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
src/almanac_contracts/__init__.py
|
|
3
|
+
src/almanac_contracts/errors.py
|
|
4
|
+
src/almanac_contracts/models.py
|
|
5
|
+
src/almanac_contracts/urls.py
|
|
6
|
+
src/almanac_contracts.egg-info/PKG-INFO
|
|
7
|
+
src/almanac_contracts.egg-info/SOURCES.txt
|
|
8
|
+
src/almanac_contracts.egg-info/dependency_links.txt
|
|
9
|
+
src/almanac_contracts.egg-info/requires.txt
|
|
10
|
+
src/almanac_contracts.egg-info/top_level.txt
|
|
11
|
+
src/almanac_contracts/hosted/__init__.py
|
|
12
|
+
src/almanac_contracts/hosted/api_keys.py
|
|
13
|
+
src/almanac_contracts/hosted/billing.py
|
|
14
|
+
src/almanac_contracts/hosted/jobs.py
|
|
15
|
+
src/almanac_contracts/hosted/pages.py
|
|
16
|
+
src/almanac_contracts/hosted/sources.py
|
|
17
|
+
src/almanac_contracts/hosted/status.py
|
|
18
|
+
src/almanac_contracts/hosted/topics.py
|
|
19
|
+
src/almanac_contracts/hosted/wikis.py
|
|
20
|
+
src/almanac_contracts/hosted/workspaces.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pydantic>=2.8
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
almanac_contracts
|