| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613 |
- from __future__ import annotations
- import typing as t
- from dataclasses import asdict, dataclass, field
- from enum import Enum
- from rdflib import Literal, URIRef
- from rdflib.util import from_n3
- @dataclass(frozen=True)
- class TopologyStatus:
- state: str
- primaryTags: dict[str, t.Any] # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- state = t.cast(t.Any, self.state)
- primary_tags = t.cast(t.Any, self.primaryTags)
- if not isinstance(state, str):
- invalid.append(("state", state, type(state)))
- if not isinstance(primary_tags, dict):
- invalid.append(("primaryTags", primary_tags, type(primary_tags)))
- else:
- for key, value in primary_tags.items():
- if not isinstance(key, str):
- invalid.append((f"primaryTags key '{key}'", key, type(key)))
- if invalid:
- raise ValueError("Invalid TopologyStatus values: ", invalid)
- @dataclass(frozen=True)
- class RecoveryOperation:
- name: str
- message: str
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- name = t.cast(t.Any, self.name)
- message = t.cast(t.Any, self.message)
- if not isinstance(name, str):
- invalid.append(("name", name, type(name)))
- if not isinstance(message, str):
- invalid.append(("message", message, type(message)))
- if invalid:
- raise ValueError("Invalid RecoveryOperation values: ", invalid)
- @dataclass(frozen=True)
- class RecoveryStatus:
- state: RecoveryOperation | None = None
- message: str | None = None
- affectedNodes: list[str] = field(default_factory=list) # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- state = t.cast(t.Any, self.state)
- message = t.cast(t.Any, self.message)
- affected_nodes = t.cast(t.Any, self.affectedNodes)
- if state is not None and not isinstance(state, RecoveryOperation):
- invalid.append(("state", state, type(state)))
- if message is not None and not isinstance(message, str):
- invalid.append(("message", message, type(message)))
- if not isinstance(affected_nodes, list):
- invalid.append(("affectedNodes", affected_nodes, type(affected_nodes)))
- else:
- for index, value in enumerate(affected_nodes):
- if not isinstance(value, str):
- invalid.append((f"affectedNodes[{index}]", value, type(value)))
- if invalid:
- raise ValueError("Invalid RecoveryStatus values: ", invalid)
- @classmethod
- def from_dict(cls, data: dict) -> RecoveryStatus:
- """Create a RecoveryStatus instance from a dict.
- This is useful for converting JSON response data into the dataclass structure.
- Handles empty dict {} by returning a RecoveryStatus with all None/default values.
- The nested 'state' dict (if present) is automatically converted to
- a RecoveryOperation instance.
- Parameters:
- data: A dict containing the recovery status data, typically
- parsed from a JSON response. Can be an empty dict.
- Returns:
- A RecoveryStatus instance with nested dataclass objects.
- Raises:
- KeyError: If required keys are missing from the input dict.
- TypeError: If nested 'state' cannot be unpacked into RecoveryOperation.
- ValueError: If field validation fails in RecoveryOperation or
- RecoveryStatus (e.g., invalid types).
- """
- # Handle empty dict case
- if not data:
- return cls()
- state = None
- if "state" in data and data["state"] is not None:
- state = RecoveryOperation(**data["state"])
- return cls(
- state=state,
- message=data.get("message"),
- affectedNodes=data.get("affectedNodes", []),
- )
- @dataclass(frozen=True)
- class NodeStatus:
- address: str
- nodeState: str # noqa: N815
- term: int
- syncStatus: dict[str, str] # noqa: N815
- lastLogTerm: int # noqa: N815
- lastLogIndex: int # noqa: N815
- endpoint: str
- recoveryStatus: RecoveryStatus | None # noqa: N815
- topologyStatus: TopologyStatus | None # noqa: N815
- clusterEnabled: bool | None # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- address = t.cast(t.Any, self.address)
- node_state = t.cast(t.Any, self.nodeState)
- term = t.cast(t.Any, self.term)
- sync_status = t.cast(t.Any, self.syncStatus)
- last_log_term = t.cast(t.Any, self.lastLogTerm)
- last_log_index = t.cast(t.Any, self.lastLogIndex)
- endpoint = t.cast(t.Any, self.endpoint)
- recovery_status = t.cast(t.Any, self.recoveryStatus)
- topology_status = t.cast(t.Any, self.topologyStatus)
- cluster_enabled = t.cast(t.Any, self.clusterEnabled)
- if not isinstance(address, str):
- invalid.append(("address", address, type(address)))
- if not isinstance(node_state, str):
- invalid.append(("nodeState", node_state, type(node_state)))
- if type(term) is not int:
- invalid.append(("term", term, type(term)))
- if not isinstance(sync_status, dict):
- invalid.append(("syncStatus", sync_status, type(sync_status)))
- else:
- for key, value in sync_status.items():
- if not isinstance(key, str):
- invalid.append((f"syncStatus key '{key}'", key, type(key)))
- if not isinstance(value, str):
- invalid.append((f"syncStatus['{key}']", value, type(value)))
- if type(last_log_term) is not int:
- invalid.append(("lastLogTerm", last_log_term, type(last_log_term)))
- if type(last_log_index) is not int:
- invalid.append(("lastLogIndex", last_log_index, type(last_log_index)))
- if not isinstance(endpoint, str):
- invalid.append(("endpoint", endpoint, type(endpoint)))
- if recovery_status is not None and not isinstance(
- recovery_status, RecoveryStatus
- ):
- invalid.append(("recoveryStatus", recovery_status, type(recovery_status)))
- if topology_status is not None and not isinstance(
- topology_status, TopologyStatus
- ):
- invalid.append(("topologyStatus", topology_status, type(topology_status)))
- if cluster_enabled is not None and type(cluster_enabled) is not bool:
- invalid.append(("clusterEnabled", cluster_enabled, type(cluster_enabled)))
- if invalid:
- raise ValueError("Invalid NodeStatus values: ", invalid)
- @classmethod
- def from_dict(cls, data: dict) -> NodeStatus:
- """Create a NodeStatus instance from a dict.
- This is useful for converting JSON response data into the dataclass structure.
- The nested 'recoveryStatus' and 'topologyStatus' dicts are automatically
- converted to their respective dataclass instances.
- Parameters:
- data: A dict containing the node status data, typically
- parsed from a JSON response.
- Returns:
- A NodeStatus instance with nested dataclass objects.
- Raises:
- KeyError: If required keys are missing from the input dict.
- TypeError: If nested dicts cannot be unpacked into their respective
- dataclass instances.
- ValueError: If field validation fails in TopologyStatus, RecoveryOperation,
- RecoveryStatus, or NodeStatus (e.g., invalid types).
- """
- # Handle recoveryStatus - can be empty dict or actual data
- recovery_status = None
- if "recoveryStatus" in data and data["recoveryStatus"]:
- recovery_status = RecoveryStatus.from_dict(data["recoveryStatus"])
- elif "recoveryStatus" in data:
- # Empty dict case
- recovery_status = RecoveryStatus.from_dict({})
- # Handle topologyStatus - optional field
- topology_status = None
- if "topologyStatus" in data and data["topologyStatus"]:
- topology_status = TopologyStatus(**data["topologyStatus"])
- return cls(
- address=data["address"],
- nodeState=data["nodeState"],
- term=data["term"],
- syncStatus=data["syncStatus"],
- lastLogTerm=data["lastLogTerm"],
- lastLogIndex=data["lastLogIndex"],
- endpoint=data["endpoint"],
- recoveryStatus=recovery_status,
- topologyStatus=topology_status,
- clusterEnabled=data.get("clusterEnabled"),
- )
- @dataclass(frozen=True)
- class ClusterRequest:
- electionMinTimeout: int # noqa: N815
- electionRangeTimeout: int # noqa: N815
- heartbeatInterval: int # noqa: N815
- messageSizeKB: int # noqa: N815
- verificationTimeout: int # noqa: N815
- transactionLogMaximumSizeGB: int # noqa: N815
- batchUpdateInterval: int # noqa: N815
- nodes: list[str]
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- election_min_timeout = t.cast(t.Any, self.electionMinTimeout)
- election_range_timeout = t.cast(t.Any, self.electionRangeTimeout)
- heartbeat_interval = t.cast(t.Any, self.heartbeatInterval)
- message_size_kb = t.cast(t.Any, self.messageSizeKB)
- verification_timeout = t.cast(t.Any, self.verificationTimeout)
- transaction_log_maximum_size_gb = t.cast(
- t.Any, self.transactionLogMaximumSizeGB
- )
- batch_update_interval = t.cast(t.Any, self.batchUpdateInterval)
- nodes = t.cast(t.Any, self.nodes)
- if type(election_min_timeout) is not int:
- invalid.append(
- ("electionMinTimeout", election_min_timeout, type(election_min_timeout))
- )
- if type(election_range_timeout) is not int:
- invalid.append(
- (
- "electionRangeTimeout",
- election_range_timeout,
- type(election_range_timeout),
- )
- )
- if type(heartbeat_interval) is not int:
- invalid.append(
- ("heartbeatInterval", heartbeat_interval, type(heartbeat_interval))
- )
- if type(message_size_kb) is not int:
- invalid.append(("messageSizeKB", message_size_kb, type(message_size_kb)))
- if type(verification_timeout) is not int:
- invalid.append(
- (
- "verificationTimeout",
- verification_timeout,
- type(verification_timeout),
- )
- )
- if type(transaction_log_maximum_size_gb) is not int:
- invalid.append(
- (
- "transactionLogMaximumSizeGB",
- transaction_log_maximum_size_gb,
- type(transaction_log_maximum_size_gb),
- )
- )
- if type(batch_update_interval) is not int:
- invalid.append(
- (
- "batchUpdateInterval",
- batch_update_interval,
- type(batch_update_interval),
- )
- )
- if not isinstance(nodes, list):
- invalid.append(("nodes", nodes, type(nodes)))
- else:
- for index, value in enumerate(nodes):
- if not isinstance(value, str):
- invalid.append((f"nodes[{index}]", value, type(value)))
- if invalid:
- raise ValueError("Invalid ClusterRequest values: ", invalid)
- @classmethod
- def from_dict(cls, data: dict) -> ClusterRequest:
- """Create a ClusterRequest instance from a dict.
- This is useful for converting JSON response data into the dataclass structure.
- Parameters:
- data: A dict containing the cluster request data, typically
- parsed from a JSON response.
- Returns:
- A ClusterRequest instance.
- Raises:
- KeyError: If required keys are missing from the input dict.
- TypeError: If the dict cannot be unpacked into ClusterRequest.
- ValueError: If field validation fails in ClusterRequest
- (e.g., invalid types for integer fields or nodes).
- """
- return cls(
- electionMinTimeout=data["electionMinTimeout"],
- electionRangeTimeout=data["electionRangeTimeout"],
- heartbeatInterval=data["heartbeatInterval"],
- messageSizeKB=data["messageSizeKB"],
- verificationTimeout=data["verificationTimeout"],
- transactionLogMaximumSizeGB=data["transactionLogMaximumSizeGB"],
- batchUpdateInterval=data["batchUpdateInterval"],
- nodes=data["nodes"],
- )
- @dataclass(frozen=True)
- class SnapshotOptionsBean:
- withRepositoryData: bool # noqa: N815
- withSystemData: bool # noqa: N815
- cleanDataDir: bool # noqa: N815
- repositories: list[str] | None = None
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- with_repository_data = t.cast(t.Any, self.withRepositoryData)
- with_system_data = t.cast(t.Any, self.withSystemData)
- clean_data_dir = t.cast(t.Any, self.cleanDataDir)
- repositories = t.cast(t.Any, self.repositories)
- if type(with_repository_data) is not bool:
- invalid.append(
- ("withRepositoryData", with_repository_data, type(with_repository_data))
- )
- if type(with_system_data) is not bool:
- invalid.append(("withSystemData", with_system_data, type(with_system_data)))
- if type(clean_data_dir) is not bool:
- invalid.append(("cleanDataDir", clean_data_dir, type(clean_data_dir)))
- if repositories is not None:
- if not isinstance(repositories, list):
- invalid.append(("repositories", repositories, type(repositories)))
- else:
- for index, value in enumerate(repositories):
- if not isinstance(value, str):
- invalid.append((f"repositories[{index}]", value, type(value)))
- if invalid:
- raise ValueError("Invalid SnapshotOptionsBean values: ", invalid)
- @dataclass(frozen=True)
- class BackupOperationBean:
- id: str
- username: str
- operation: t.Literal[
- "CREATE_BACKUP_IN_PROGRESS",
- "RESTORE_BACKUP_IN_PROGRESS",
- "CREATE_CLOUD_BACKUP_IN_PROGRESS",
- "RESTORE_CLOUD_BACKUP_IN_PROGRESS",
- ]
- affectedRepositories: list[str] # noqa: N815
- msSinceCreated: int # noqa: N815
- snapshotOptions: SnapshotOptionsBean # noqa: N815
- nodePerformingClusterBackup: str | None = None # noqa: N815
- def __post_init__(self) -> None:
- _allowed_operations = {
- "CREATE_BACKUP_IN_PROGRESS",
- "RESTORE_BACKUP_IN_PROGRESS",
- "CREATE_CLOUD_BACKUP_IN_PROGRESS",
- "RESTORE_CLOUD_BACKUP_IN_PROGRESS",
- }
- invalid: list[tuple[str, t.Any, type]] = []
- id_ = t.cast(t.Any, self.id)
- username = t.cast(t.Any, self.username)
- operation = t.cast(t.Any, self.operation)
- affected_repositories = t.cast(t.Any, self.affectedRepositories)
- ms_since_created = t.cast(t.Any, self.msSinceCreated)
- snapshot_options = t.cast(t.Any, self.snapshotOptions)
- node_performing_cluster_backup = t.cast(t.Any, self.nodePerformingClusterBackup)
- if not isinstance(id_, str):
- invalid.append(("id", id_, type(id_)))
- if not isinstance(username, str):
- invalid.append(("username", username, type(username)))
- if not isinstance(operation, str) or operation not in _allowed_operations:
- invalid.append(("operation", operation, type(operation)))
- if not isinstance(affected_repositories, list):
- invalid.append(
- (
- "affectedRepositories",
- affected_repositories,
- type(affected_repositories),
- )
- )
- else:
- for index, value in enumerate(affected_repositories):
- if not isinstance(value, str):
- invalid.append(
- (f"affectedRepositories[{index}]", value, type(value))
- )
- if type(ms_since_created) is not int:
- invalid.append(("msSinceCreated", ms_since_created, type(ms_since_created)))
- if not isinstance(snapshot_options, SnapshotOptionsBean):
- invalid.append(
- ("snapshotOptions", snapshot_options, type(snapshot_options))
- )
- if node_performing_cluster_backup is not None and not isinstance(
- node_performing_cluster_backup, str
- ):
- invalid.append(
- (
- "nodePerformingClusterBackup",
- node_performing_cluster_backup,
- type(node_performing_cluster_backup),
- )
- )
- if invalid:
- raise ValueError("Invalid BackupOperationBean values: ", invalid)
- @classmethod
- def from_dict(cls, data: dict) -> BackupOperationBean:
- """Create a BackupOperationBean instance from a dict.
- This is useful for converting JSON response data into the dataclass structure.
- The nested 'snapshotOptions' dict is automatically converted to
- a SnapshotOptionsBean instance.
- Parameters:
- data: A dict containing the backup operation data, typically
- parsed from a JSON response.
- Returns:
- A BackupOperationBean instance with nested dataclass objects.
- Raises:
- KeyError: If required keys are missing from the input dict.
- TypeError: If nested 'snapshotOptions' cannot be unpacked into
- SnapshotOptionsBean.
- ValueError: If field validation fails in SnapshotOptionsBean or
- BackupOperationBean (e.g., invalid types or operation values).
- """
- snapshot_options = SnapshotOptionsBean(**data["snapshotOptions"])
- return cls(
- id=data["id"],
- username=data["username"],
- operation=data["operation"],
- affectedRepositories=data["affectedRepositories"],
- msSinceCreated=data["msSinceCreated"],
- snapshotOptions=snapshot_options,
- nodePerformingClusterBackup=data.get("nodePerformingClusterBackup"),
- )
- @dataclass(frozen=True)
- class StructuresStatistics:
- cacheHit: int # noqa: N815
- cacheMiss: int # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- cache_hit = t.cast(t.Any, self.cacheHit)
- cache_miss = t.cast(t.Any, self.cacheMiss)
- if type(cache_hit) is not int:
- invalid.append(("cacheHit", cache_hit, type(cache_hit)))
- if type(cache_miss) is not int:
- invalid.append(("cacheMiss", cache_miss, type(cache_miss)))
- if invalid:
- raise ValueError("Invalid StructuresStatistics values: ", invalid)
- @dataclass(frozen=True)
- class RepositoryStatisticsQueries:
- slow: int
- suboptimal: int
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- slow = t.cast(t.Any, self.slow)
- suboptimal = t.cast(t.Any, self.suboptimal)
- if type(slow) is not int:
- invalid.append(("slow", slow, type(slow)))
- if type(suboptimal) is not int:
- invalid.append(("suboptimal", suboptimal, type(suboptimal)))
- if invalid:
- raise ValueError("Invalid RepositoryStatisticsQueries values: ", invalid)
- @dataclass(frozen=True)
- class RepositoryStatisticsEntityPool:
- epoolReads: int # noqa: N815
- epoolWrites: int # noqa: N815
- epoolSize: int # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- epool_reads = t.cast(t.Any, self.epoolReads)
- epool_writes = t.cast(t.Any, self.epoolWrites)
- epool_size = t.cast(t.Any, self.epoolSize)
- if type(epool_reads) is not int:
- invalid.append(("epoolReads", epool_reads, type(epool_reads)))
- if type(epool_writes) is not int:
- invalid.append(("epoolWrites", epool_writes, type(epool_writes)))
- if type(epool_size) is not int:
- invalid.append(("epoolSize", epool_size, type(epool_size)))
- if invalid:
- raise ValueError("Invalid RepositoryStatisticsEntityPool values: ", invalid)
- @dataclass(frozen=True)
- class RepositoryStatistics:
- queries: RepositoryStatisticsQueries
- entityPool: RepositoryStatisticsEntityPool # noqa: N815
- activeTransactions: int # noqa: N815
- openConnections: int # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- queries = t.cast(t.Any, self.queries)
- entity_pool = t.cast(t.Any, self.entityPool)
- active_transactions = t.cast(t.Any, self.activeTransactions)
- open_connections = t.cast(t.Any, self.openConnections)
- if not isinstance(queries, RepositoryStatisticsQueries):
- invalid.append(("queries", queries, type(queries)))
- if not isinstance(entity_pool, RepositoryStatisticsEntityPool):
- invalid.append(("entityPool", entity_pool, type(entity_pool)))
- if type(active_transactions) is not int:
- invalid.append(
- ("activeTransactions", active_transactions, type(active_transactions))
- )
- if type(open_connections) is not int:
- invalid.append(
- ("openConnections", open_connections, type(open_connections))
- )
- if invalid:
- raise ValueError("Invalid RepositoryStatistics values: ", invalid)
- @classmethod
- def from_dict(cls, data: dict) -> RepositoryStatistics:
- """Create a RepositoryStatistics instance from a dict.
- This is useful for converting JSON response data into the dataclass structure.
- The nested 'queries' and 'entityPool' dicts are automatically converted to
- their respective dataclass instances.
- Parameters:
- data: A dict containing the repository statistics data, typically
- parsed from a JSON response.
- Returns:
- A RepositoryStatistics instance with nested dataclass objects.
- Raises:
- KeyError: If required keys are missing from the input dict.
- TypeError: If nested dicts cannot be unpacked into their respective
- dataclass instances.
- ValueError: If field validation fails in RepositoryStatisticsQueries,
- RepositoryStatisticsEntityPool, or RepositoryStatistics
- (e.g., invalid types).
- """
- queries = RepositoryStatisticsQueries(**data["queries"])
- entity_pool = RepositoryStatisticsEntityPool(**data["entityPool"])
- return cls(
- queries=queries,
- entityPool=entity_pool,
- activeTransactions=data["activeTransactions"],
- openConnections=data["openConnections"],
- )
- @dataclass(frozen=True)
- class InfrastructureMemoryUsage:
- max: int
- committed: int
- init: int
- used: int
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- max_val = t.cast(t.Any, self.max)
- committed = t.cast(t.Any, self.committed)
- init = t.cast(t.Any, self.init)
- used = t.cast(t.Any, self.used)
- if type(max_val) is not int:
- invalid.append(("max", max_val, type(max_val)))
- if type(committed) is not int:
- invalid.append(("committed", committed, type(committed)))
- if type(init) is not int:
- invalid.append(("init", init, type(init)))
- if type(used) is not int:
- invalid.append(("used", used, type(used)))
- if invalid:
- raise ValueError("Invalid InfrastructureMemoryUsage values: ", invalid)
- @dataclass(frozen=True)
- class InfrastructureStorageMemory:
- dataDirUsed: int # noqa: N815
- workDirUsed: int # noqa: N815
- logsDirUsed: int # noqa: N815
- dataDirFree: int # noqa: N815
- workDirFree: int # noqa: N815
- logsDirFree: int # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- data_dir_used = t.cast(t.Any, self.dataDirUsed)
- work_dir_used = t.cast(t.Any, self.workDirUsed)
- logs_dir_used = t.cast(t.Any, self.logsDirUsed)
- data_dir_free = t.cast(t.Any, self.dataDirFree)
- work_dir_free = t.cast(t.Any, self.workDirFree)
- logs_dir_free = t.cast(t.Any, self.logsDirFree)
- if type(data_dir_used) is not int:
- invalid.append(("dataDirUsed", data_dir_used, type(data_dir_used)))
- if type(work_dir_used) is not int:
- invalid.append(("workDirUsed", work_dir_used, type(work_dir_used)))
- if type(logs_dir_used) is not int:
- invalid.append(("logsDirUsed", logs_dir_used, type(logs_dir_used)))
- if type(data_dir_free) is not int:
- invalid.append(("dataDirFree", data_dir_free, type(data_dir_free)))
- if type(work_dir_free) is not int:
- invalid.append(("workDirFree", work_dir_free, type(work_dir_free)))
- if type(logs_dir_free) is not int:
- invalid.append(("logsDirFree", logs_dir_free, type(logs_dir_free)))
- if invalid:
- raise ValueError("Invalid InfrastructureStorageMemory values: ", invalid)
- @dataclass(frozen=True)
- class InfrastructureStatistics:
- heapMemoryUsage: InfrastructureMemoryUsage # noqa: N815
- nonHeapMemoryUsage: InfrastructureMemoryUsage # noqa: N815
- storageMemory: InfrastructureStorageMemory # noqa: N815
- threadCount: int # noqa: N815
- cpuLoad: float # noqa: N815
- classCount: int # noqa: N815
- gcCount: int # noqa: N815
- openFileDescriptors: int # noqa: N815
- maxFileDescriptors: int # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- heap_memory_usage = t.cast(t.Any, self.heapMemoryUsage)
- non_heap_memory_usage = t.cast(t.Any, self.nonHeapMemoryUsage)
- storage_memory = t.cast(t.Any, self.storageMemory)
- thread_count = t.cast(t.Any, self.threadCount)
- cpu_load = t.cast(t.Any, self.cpuLoad)
- class_count = t.cast(t.Any, self.classCount)
- gc_count = t.cast(t.Any, self.gcCount)
- open_file_descriptors = t.cast(t.Any, self.openFileDescriptors)
- max_file_descriptors = t.cast(t.Any, self.maxFileDescriptors)
- if not isinstance(heap_memory_usage, InfrastructureMemoryUsage):
- invalid.append(
- ("heapMemoryUsage", heap_memory_usage, type(heap_memory_usage))
- )
- if not isinstance(non_heap_memory_usage, InfrastructureMemoryUsage):
- invalid.append(
- (
- "nonHeapMemoryUsage",
- non_heap_memory_usage,
- type(non_heap_memory_usage),
- )
- )
- if not isinstance(storage_memory, InfrastructureStorageMemory):
- invalid.append(("storageMemory", storage_memory, type(storage_memory)))
- if type(thread_count) is not int:
- invalid.append(("threadCount", thread_count, type(thread_count)))
- if type(cpu_load) is not float and type(cpu_load) is not int:
- invalid.append(("cpuLoad", cpu_load, type(cpu_load)))
- if type(class_count) is not int:
- invalid.append(("classCount", class_count, type(class_count)))
- if type(gc_count) is not int:
- invalid.append(("gcCount", gc_count, type(gc_count)))
- if type(open_file_descriptors) is not int:
- invalid.append(
- (
- "openFileDescriptors",
- open_file_descriptors,
- type(open_file_descriptors),
- )
- )
- if type(max_file_descriptors) is not int:
- invalid.append(
- ("maxFileDescriptors", max_file_descriptors, type(max_file_descriptors))
- )
- if invalid:
- raise ValueError("Invalid InfrastructureStatistics values: ", invalid)
- @classmethod
- def from_dict(cls, data: dict) -> InfrastructureStatistics:
- """Create an InfrastructureStatistics instance from a dict.
- This is useful for converting JSON response data into the dataclass structure.
- The nested memory and storage dicts are automatically converted to their
- respective dataclass instances.
- Parameters:
- data: A dict containing the infrastructure statistics data, typically
- parsed from a JSON response.
- Returns:
- An InfrastructureStatistics instance with nested dataclass objects.
- Raises:
- KeyError: If required keys are missing from the input dict.
- TypeError: If nested dicts cannot be unpacked into their respective
- dataclass instances.
- ValueError: If field validation fails in InfrastructureMemoryUsage,
- InfrastructureStorageMemory, or InfrastructureStatistics
- (e.g., invalid types).
- """
- heap_memory_usage = InfrastructureMemoryUsage(**data["heapMemoryUsage"])
- non_heap_memory_usage = InfrastructureMemoryUsage(**data["nonHeapMemoryUsage"])
- storage_memory = InfrastructureStorageMemory(**data["storageMemory"])
- return cls(
- heapMemoryUsage=heap_memory_usage,
- nonHeapMemoryUsage=non_heap_memory_usage,
- storageMemory=storage_memory,
- threadCount=data["threadCount"],
- cpuLoad=float(data["cpuLoad"]),
- classCount=data["classCount"],
- gcCount=data["gcCount"],
- openFileDescriptors=data["openFileDescriptors"],
- maxFileDescriptors=data["maxFileDescriptors"],
- )
- @dataclass(frozen=True)
- class ParserSettings:
- preserveBNodeIds: bool = False # noqa: N815
- failOnUnknownDataTypes: bool = False # noqa: N815
- verifyDataTypeValues: bool = False # noqa: N815
- normalizeDataTypeValues: bool = False # noqa: N815
- failOnUnknownLanguageTags: bool = False # noqa: N815
- verifyLanguageTags: bool = True # noqa: N815
- normalizeLanguageTags: bool = False # noqa: N815
- stopOnError: bool = True # noqa: N815
- contextLink: t.Any | None = None # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- preserve_bnode_ids = t.cast(t.Any, self.preserveBNodeIds)
- fail_on_unknown_data_types = t.cast(t.Any, self.failOnUnknownDataTypes)
- verify_data_type_values = t.cast(t.Any, self.verifyDataTypeValues)
- normalize_data_type_values = t.cast(t.Any, self.normalizeDataTypeValues)
- fail_on_unknown_language_tags = t.cast(t.Any, self.failOnUnknownLanguageTags)
- verify_language_tags = t.cast(t.Any, self.verifyLanguageTags)
- normalize_language_tags = t.cast(t.Any, self.normalizeLanguageTags)
- stop_on_error = t.cast(t.Any, self.stopOnError)
- if type(preserve_bnode_ids) is not bool:
- invalid.append(
- ("preserveBNodeIds", preserve_bnode_ids, type(preserve_bnode_ids))
- )
- if type(fail_on_unknown_data_types) is not bool:
- invalid.append(
- (
- "failOnUnknownDataTypes",
- fail_on_unknown_data_types,
- type(fail_on_unknown_data_types),
- )
- )
- if type(verify_data_type_values) is not bool:
- invalid.append(
- (
- "verifyDataTypeValues",
- verify_data_type_values,
- type(verify_data_type_values),
- )
- )
- if type(normalize_data_type_values) is not bool:
- invalid.append(
- (
- "normalizeDataTypeValues",
- normalize_data_type_values,
- type(normalize_data_type_values),
- )
- )
- if type(fail_on_unknown_language_tags) is not bool:
- invalid.append(
- (
- "failOnUnknownLanguageTags",
- fail_on_unknown_language_tags,
- type(fail_on_unknown_language_tags),
- )
- )
- if type(verify_language_tags) is not bool:
- invalid.append(
- ("verifyLanguageTags", verify_language_tags, type(verify_language_tags))
- )
- if type(normalize_language_tags) is not bool:
- invalid.append(
- (
- "normalizeLanguageTags",
- normalize_language_tags,
- type(normalize_language_tags),
- )
- )
- if type(stop_on_error) is not bool:
- invalid.append(("stopOnError", stop_on_error, type(stop_on_error)))
- # Note: don't check contextLink here since it's of type t.Any.
- if invalid:
- raise ValueError("Invalid ParserSettings values: ", invalid)
- def as_dict(self) -> dict[str, t.Any]:
- return asdict(self)
- @dataclass(frozen=True)
- class ImportSettings:
- name: str
- status: t.Literal["PENDING", "IMPORTING", "DONE", "ERROR", "NONE", "INTERRUPTING"]
- size: str
- lastModified: int # noqa: N815
- imported: int
- addedStatements: int # noqa: N815
- removedStatements: int # noqa: N815
- numReplacedGraphs: int # noqa: N815
- message: str = ""
- context: t.Any | None = None
- replaceGraphs: t.List = field(default_factory=list) # noqa: N815
- baseURI: t.Any | None = None # noqa: N815
- forceSerial: bool = False # noqa: N815
- type: str = "file"
- format: t.Any | None = None
- data: t.Any | None = None
- parserSettings: ParserSettings = field(default_factory=ParserSettings) # noqa: N815
- def __post_init__(self) -> None:
- _allowed_status = {
- "PENDING",
- "IMPORTING",
- "DONE",
- "ERROR",
- "NONE",
- "INTERRUPTING",
- }
- invalid: list[tuple[str, t.Any, type]] = []
- name = t.cast(t.Any, self.name)
- status = t.cast(t.Any, self.status)
- message = t.cast(t.Any, self.message)
- replace_graphs = t.cast(t.Any, self.replaceGraphs)
- force_serial = t.cast(t.Any, self.forceSerial)
- type_ = t.cast(t.Any, self.type)
- parser_settings = t.cast(t.Any, self.parserSettings)
- size = t.cast(t.Any, self.size)
- last_modified = t.cast(t.Any, self.lastModified)
- imported = t.cast(t.Any, self.imported)
- added_statements = t.cast(t.Any, self.addedStatements)
- removed_statements = t.cast(t.Any, self.removedStatements)
- num_replaced_graphs = t.cast(t.Any, self.numReplacedGraphs)
- if not isinstance(name, str):
- invalid.append(("name", name, type(name)))
- if status not in _allowed_status:
- invalid.append(("status", status, type(status)))
- if not isinstance(message, str):
- invalid.append(("message", message, type(message)))
- if not isinstance(replace_graphs, list):
- invalid.append(("replaceGraphs", replace_graphs, type(replace_graphs)))
- if type(force_serial) is not bool:
- invalid.append(("forceSerial", force_serial, type(force_serial)))
- if not isinstance(type_, str):
- invalid.append(("type", type_, type(type_)))
- if not isinstance(parser_settings, ParserSettings):
- invalid.append(("parserSettings", parser_settings, type(parser_settings)))
- if not isinstance(size, str):
- invalid.append(("size", size, type(size)))
- if type(last_modified) is not int:
- invalid.append(("lastModified", last_modified, type(last_modified)))
- if type(imported) is not int:
- invalid.append(("imported", imported, type(imported)))
- if type(added_statements) is not int:
- invalid.append(
- ("addedStatements", added_statements, type(added_statements))
- )
- if type(removed_statements) is not int:
- invalid.append(
- ("removedStatements", removed_statements, type(removed_statements))
- )
- if type(num_replaced_graphs) is not int:
- invalid.append(
- ("numReplacedGraphs", num_replaced_graphs, type(num_replaced_graphs))
- )
- # Note: don't check context, baseURI, format, or data here since they are of type t.Any.
- if invalid:
- raise ValueError("Invalid ImportSettings values: ", invalid)
- def as_dict(self) -> dict[str, t.Any]:
- return asdict(self)
- @dataclass(frozen=True)
- class ServerImportBody:
- fileNames: list[str] # noqa: N815
- importSettings: ImportSettings | None = None # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- import_settings = t.cast(t.Any, self.importSettings)
- file_names = t.cast(t.Any, self.fileNames)
- if import_settings is not None and not isinstance(
- import_settings, ImportSettings
- ):
- invalid.append(("importSettings", import_settings, type(import_settings)))
- if not isinstance(file_names, list):
- invalid.append(("fileNames", file_names, type(file_names)))
- else:
- for index, value in enumerate(file_names):
- if not isinstance(value, str):
- invalid.append((f"fileNames[{index}]", value, type(value)))
- if invalid:
- raise ValueError("Invalid ServerImportBody values: ", invalid)
- def as_dict(self) -> dict[str, t.Any]:
- result = asdict(self)
- if self.importSettings is None:
- result.pop("importSettings", None)
- return result
- @dataclass(frozen=True)
- class UserUpdate:
- password: str = field(default="")
- appSettings: dict[str, t.Any] = field(default_factory=dict) # noqa: N815
- gptThreads: list[t.Any] = field(default_factory=list) # noqa: N815
- def as_dict(self) -> dict[str, t.Any]:
- return asdict(self)
- @dataclass(frozen=True)
- class UserCreate:
- """Dataclass for creating a new user in GraphDB.
- Unlike `User`, this class does not include `dateCreated` since
- GraphDB automatically assigns this value when the user is created.
- """
- username: str
- password: str
- grantedAuthorities: list[str] = field(default_factory=list) # noqa: N815
- appSettings: dict[str, t.Any] = field(default_factory=dict) # noqa: N815
- gptThreads: list[t.Any] = field(default_factory=list) # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- username = t.cast(t.Any, self.username)
- password = t.cast(t.Any, self.password)
- granted_authorities = t.cast(t.Any, self.grantedAuthorities)
- app_settings = t.cast(t.Any, self.appSettings)
- gpt_threads = t.cast(t.Any, self.gptThreads)
- if not isinstance(username, str):
- invalid.append(("username", username, type(username)))
- if not isinstance(password, str):
- invalid.append(("password", password, type(password)))
- if not isinstance(granted_authorities, list):
- invalid.append(
- ("grantedAuthorities", granted_authorities, type(granted_authorities))
- )
- else:
- for index, value in enumerate(granted_authorities):
- if not isinstance(value, str):
- invalid.append((f"grantedAuthorities[{index}]", value, type(value)))
- if not isinstance(app_settings, dict):
- invalid.append(("appSettings", app_settings, type(app_settings)))
- else:
- for key in app_settings.keys():
- if not isinstance(key, str):
- invalid.append(("appSettings key", key, type(key)))
- break
- if not isinstance(gpt_threads, list):
- invalid.append(("gptThreads", gpt_threads, type(gpt_threads)))
- if invalid:
- raise ValueError("Invalid UserCreate values: ", invalid)
- def as_dict(self) -> dict[str, t.Any]:
- return asdict(self)
- @dataclass(frozen=True)
- class User:
- username: str
- password: str
- dateCreated: int # noqa: N815
- grantedAuthorities: list[str] = field(default_factory=list) # noqa: N815
- appSettings: dict[str, t.Any] = field(default_factory=dict) # noqa: N815
- gptThreads: list[t.Any] = field(default_factory=list) # noqa: N815
- def __post_init__(self) -> None:
- # Normalize None values from API responses to empty collections
- if self.grantedAuthorities is None:
- object.__setattr__(self, "grantedAuthorities", []) # type: ignore[unreachable]
- if self.appSettings is None:
- object.__setattr__(self, "appSettings", {}) # type: ignore[unreachable]
- if self.gptThreads is None:
- object.__setattr__(self, "gptThreads", []) # type: ignore[unreachable]
- invalid: list[tuple[str, t.Any, type]] = []
- username = t.cast(t.Any, self.username)
- password = t.cast(t.Any, self.password)
- date_created = t.cast(t.Any, self.dateCreated)
- granted_authorities = t.cast(t.Any, self.grantedAuthorities)
- app_settings = t.cast(t.Any, self.appSettings)
- gpt_threads = t.cast(t.Any, self.gptThreads)
- if not isinstance(username, str):
- invalid.append(("username", username, type(username)))
- if not isinstance(password, str):
- invalid.append(("password", password, type(password)))
- if not isinstance(date_created, int):
- invalid.append(("dateCreated", date_created, type(date_created)))
- if not isinstance(granted_authorities, list):
- invalid.append(
- ("grantedAuthorities", granted_authorities, type(granted_authorities))
- )
- else:
- for index, value in enumerate(granted_authorities):
- if not isinstance(value, str):
- invalid.append((f"grantedAuthorities[{index}]", value, type(value)))
- if not isinstance(app_settings, dict):
- invalid.append(("appSettings", app_settings, type(app_settings)))
- else:
- for key in app_settings.keys():
- if not isinstance(key, str):
- invalid.append(("appSettings key", key, type(key)))
- break
- if not isinstance(gpt_threads, list):
- invalid.append(("gptThreads", gpt_threads, type(gpt_threads)))
- if invalid:
- raise ValueError("Invalid User values: ", invalid)
- def as_dict(self):
- return asdict(self)
- @dataclass(frozen=True)
- class AuthenticatedUser:
- """Represents an authenticated user returned from POST /rest/login.
- Attributes:
- username: The username of the authenticated user.
- authorities: List of granted authorities/roles (e.g., ["ROLE_USER", "ROLE_ADMIN"]).
- appSettings: Application settings for the user.
- external: Whether the user is external (e.g., from LDAP/OAuth).
- token: The full Authorization header value (e.g., "GDB <token>").
- Can be passed directly to GraphDBClient's auth parameter.
- """
- username: str
- authorities: list[str] = field(default_factory=list)
- appSettings: dict[str, t.Any] = field(default_factory=dict) # noqa: N815
- external: bool = False
- token: str = ""
- def __post_init__(self) -> None:
- # Normalize None values from API responses to empty collections
- if self.authorities is None:
- object.__setattr__(self, "authorities", []) # type: ignore[unreachable]
- if self.appSettings is None:
- object.__setattr__(self, "appSettings", {}) # type: ignore[unreachable]
- invalid: list[tuple[str, t.Any, type]] = []
- username = t.cast(t.Any, self.username)
- authorities = t.cast(t.Any, self.authorities)
- app_settings = t.cast(t.Any, self.appSettings)
- external = t.cast(t.Any, self.external)
- token = t.cast(t.Any, self.token)
- if not isinstance(username, str):
- invalid.append(("username", username, type(username)))
- if not isinstance(authorities, list):
- invalid.append(("authorities", authorities, type(authorities)))
- else:
- for index, value in enumerate(authorities):
- if not isinstance(value, str):
- invalid.append((f"authorities[{index}]", value, type(value)))
- if not isinstance(app_settings, dict):
- invalid.append(("appSettings", app_settings, type(app_settings)))
- else:
- for key in app_settings.keys():
- if not isinstance(key, str):
- invalid.append(("appSettings key", key, type(key)))
- break
- if type(external) is not bool:
- invalid.append(("external", external, type(external)))
- if not isinstance(token, str):
- invalid.append(("token", token, type(token)))
- if invalid:
- raise ValueError("Invalid AuthenticatedUser values: ", invalid)
- @classmethod
- def from_response(cls, data: dict[str, t.Any], token: str) -> AuthenticatedUser:
- """Create an AuthenticatedUser from API response data and token.
- Parameters:
- data: The JSON response body from POST /rest/login.
- token: The GDB token extracted from the Authorization header.
- Returns:
- An AuthenticatedUser instance.
- Raises:
- ValueError: If required fields are missing or invalid.
- TypeError: If data is not a dict.
- """
- if not isinstance(data, dict):
- raise TypeError("Response data must be a dict")
- if "username" not in data:
- raise ValueError("Response data must contain 'username'")
- return cls(
- username=data["username"],
- authorities=data.get("authorities", []),
- appSettings=data.get("appSettings", {}),
- external=data.get("external", False),
- token=token,
- )
- @dataclass(frozen=True)
- class FreeAccessSettings:
- enabled: bool
- authorities: list[str] = field(default_factory=list)
- appSettings: dict[str, t.Any] = field(default_factory=dict) # noqa: N815
- def __post_init__(self) -> None:
- invalid: list[tuple[str, t.Any, type]] = []
- enabled = t.cast(t.Any, self.enabled)
- authorities = t.cast(t.Any, self.authorities)
- app_settings = t.cast(t.Any, self.appSettings)
- if type(enabled) is not bool:
- invalid.append(("enabled", enabled, type(enabled)))
- if not isinstance(authorities, list):
- invalid.append(("authorities", authorities, type(authorities)))
- else:
- for index, value in enumerate(authorities):
- if not isinstance(value, str):
- invalid.append((f"authorities[{index}]", value, type(value)))
- if not isinstance(app_settings, dict):
- invalid.append(("appSettings", app_settings, type(app_settings)))
- else:
- for key in app_settings.keys():
- if not isinstance(key, str):
- invalid.append(("appSettings key", key, type(key)))
- break
- if invalid:
- raise ValueError("Invalid FreeAccessSettings values: ", invalid)
- def as_dict(self):
- return asdict(self)
- @dataclass(frozen=True)
- class RepositorySizeInfo:
- inferred: int
- total: int
- explicit: int
- def __post_init__(self):
- invalid = []
- if type(self.inferred) is not int:
- invalid.append("inferred")
- if type(self.total) is not int:
- invalid.append("total")
- if type(self.explicit) is not int:
- invalid.append("explicit")
- if invalid:
- raise ValueError(
- "Invalid RepositorySizeInfo values: ",
- [(x, self.__dict__[x], type(self.__dict__[x])) for x in invalid],
- )
- @dataclass(frozen=True)
- class OWLimParameter:
- name: str
- label: str
- value: str
- @dataclass(frozen=True)
- class RepositoryConfigBean:
- id: str
- title: str
- type: str
- sesameType: str # noqa: N815
- location: str
- params: dict[str, OWLimParameter] = field(default_factory=dict)
- @dataclass(frozen=True)
- class RepositoryConfigBeanCreate:
- id: str
- title: str
- type: str
- sesameType: str # noqa: N815
- location: str
- params: dict[str, OWLimParameter] = field(default_factory=dict)
- missingDefaults: dict[str, OWLimParameter] = field( # noqa: N815
- default_factory=dict
- )
- def as_dict(self) -> dict:
- """Serialize the dataclass to a Python dict.
- Returns:
- dict: A dictionary representation of the dataclass suitable for use
- with httpx POST requests (e.g., via the `json` parameter).
- Examples:
- >>> config = RepositoryConfigBeanCreate(
- ... id="test-repo",
- ... title="Test Repository",
- ... type="graphdb:FreeSailRepository",
- ... sesameType="graphdb:FreeSailRepository",
- ... location="",
- ... )
- >>> config_dict = config.as_dict()
- >>> isinstance(config_dict, dict)
- True
- """
- return asdict(self)
- class RepositoryState(str, Enum):
- """Enumeration for repository state values."""
- INACTIVE = "INACTIVE"
- STARTING = "STARTING"
- RUNNING = "RUNNING"
- RESTARTING = "RESTARTING"
- STOPPING = "STOPPING"
- @dataclass
- class GraphDBRepository:
- id: t.Optional[str] = None
- title: t.Optional[str] = None
- uri: t.Optional[str] = None
- externalUrl: t.Optional[str] = None # noqa: N815
- local: t.Optional[bool] = None
- type: t.Optional[str] = None
- sesameType: t.Optional[str] = None # noqa: N815
- location: t.Optional[str] = None
- readable: t.Optional[bool] = None
- writable: t.Optional[bool] = None
- unsupported: t.Optional[bool] = None
- state: t.Optional[RepositoryState] = None
- @classmethod
- def from_dict(cls, data: dict) -> GraphDBRepository:
- """Create a GraphDBRepository instance from a dict.
- Parameters:
- data: A dict containing the repository data, typically
- parsed from a JSON response.
- Returns:
- A GraphDBRepository instance.
- Raises:
- TypeError: If the dict contains keys that do not match
- GraphDBRepository fields.
- ValueError: If the 'state' value is not a valid RepositoryState.
- """
- data_copy = dict(data)
- if "state" in data_copy and data_copy["state"] is not None:
- data_copy["state"] = RepositoryState(data_copy["state"])
- return cls(**data_copy)
- @dataclass
- class AccessControlEntry:
- scope: t.Literal["statement", "clear_graph", "plugin", "system"]
- policy: t.Literal["allow", "deny", "abstain"]
- role: str
- def as_dict(self) -> dict[str, t.Any]:
- raise NotImplementedError("Use a concrete AccessControlEntry subclass.")
- @classmethod
- def from_dict(
- cls, data: dict
- ) -> (
- SystemAccessControlEntry
- | StatementAccessControlEntry
- | PluginAccessControlEntry
- | ClearGraphAccessControlEntry
- ):
- """Create an AccessControlEntry subclass instance from raw GraphDB data.
- Note: we perform parse validation essentially twice (here and in the
- subclass's __post_init__) to ensure mypy is satisfied with the value's type.
- Parameters:
- data: A dict containing the access control entry data, typically
- parsed from a JSON response.
- Returns:
- An AccessControlEntry subclass instance (SystemAccessControlEntry,
- StatementAccessControlEntry, PluginAccessControlEntry, or
- ClearGraphAccessControlEntry) depending on the 'scope' field.
- Raises:
- TypeError: If data is not a dict.
- ValueError: If 'scope' is not a supported value ('system', 'statement',
- 'plugin', 'clear_graph'), or if any field fails validation
- (e.g., invalid policy, operation, role, plugin, subject,
- predicate, object, or graph values).
- """
- if not isinstance(data, dict):
- raise TypeError("ACL entry must be a mapping.")
- scope = data.get("scope")
- if scope == "system":
- policy = _parse_policy(data.get("policy"))
- role = _parse_role(data.get("role"))
- operation = _parse_operation(data.get("operation"))
- return SystemAccessControlEntry(
- scope="system",
- policy=policy,
- role=role,
- operation=operation,
- )
- if scope == "statement":
- policy = _parse_policy(data.get("policy"))
- role = _parse_role(data.get("role"))
- operation = _parse_operation(data.get("operation"))
- subject = _parse_subject(data.get("subject"))
- predicate = _parse_predicate(data.get("predicate"))
- obj = _parse_object(data.get("object"))
- graph = _parse_graph(data.get("context"))
- return StatementAccessControlEntry(
- scope="statement",
- policy=policy,
- role=role,
- operation=operation,
- subject=subject,
- predicate=predicate,
- object=obj,
- graph=graph,
- )
- if scope == "plugin":
- policy = _parse_policy(data.get("policy"))
- role = _parse_role(data.get("role"))
- operation = _parse_operation(data.get("operation"))
- plugin = _parse_plugin(data.get("plugin"))
- return PluginAccessControlEntry(
- scope="plugin",
- policy=policy,
- role=role,
- operation=operation,
- plugin=plugin,
- )
- if scope == "clear_graph":
- policy = _parse_policy(data.get("policy"))
- role = _parse_role(data.get("role"))
- graph = _parse_graph(data.get("context"))
- return ClearGraphAccessControlEntry(
- scope="clear_graph",
- policy=policy,
- role=role,
- graph=graph,
- )
- raise ValueError(f"Unsupported FGAC scope: {scope!r}")
- @dataclass
- class SystemAccessControlEntry(AccessControlEntry):
- scope: t.Literal["system"]
- policy: t.Literal["allow", "deny", "abstain"]
- role: str
- operation: t.Literal["read", "write", "*"]
- def __post_init__(self) -> None:
- self.policy = _parse_policy(self.policy)
- self.role = _parse_role(self.role)
- self.operation = _parse_operation(self.operation)
- def as_dict(self) -> dict[str, t.Any]:
- return {
- "scope": self.scope,
- "policy": self.policy,
- "role": self.role,
- "operation": self.operation,
- }
- @dataclass
- class StatementAccessControlEntry(AccessControlEntry):
- scope: t.Literal["statement"]
- policy: t.Literal["allow", "deny", "abstain"]
- role: str
- operation: t.Literal["read", "write", "*"]
- subject: t.Literal["*"] | URIRef
- predicate: t.Literal["*"] | URIRef
- object: t.Literal["*"] | URIRef | Literal
- graph: t.Literal["*", "named", "default"] | URIRef
- def __post_init__(self) -> None:
- self.policy = _parse_policy(self.policy)
- self.role = _parse_role(self.role)
- self.operation = _parse_operation(self.operation)
- self.subject = _parse_subject(self.subject)
- self.predicate = _parse_predicate(self.predicate)
- self.object = _parse_object(self.object)
- self.graph = _parse_graph(self.graph)
- def as_dict(self) -> dict[str, t.Any]:
- return {
- "scope": self.scope,
- "policy": self.policy,
- "role": self.role,
- "operation": self.operation,
- "subject": _format_term(self.subject),
- "predicate": _format_term(self.predicate),
- "object": _format_term(self.object),
- "context": _format_term(self.graph),
- }
- @dataclass
- class PluginAccessControlEntry(AccessControlEntry):
- scope: t.Literal["plugin"]
- policy: t.Literal["allow", "deny", "abstain"]
- role: str
- operation: t.Literal["read", "write", "*"]
- plugin: str
- def __post_init__(self) -> None:
- self.policy = _parse_policy(self.policy)
- self.role = _parse_role(self.role)
- self.operation = _parse_operation(self.operation)
- self.plugin = _parse_plugin(self.plugin)
- def as_dict(self) -> dict[str, t.Any]:
- return {
- "scope": self.scope,
- "policy": self.policy,
- "role": self.role,
- "operation": self.operation,
- "plugin": self.plugin,
- }
- @dataclass
- class ClearGraphAccessControlEntry(AccessControlEntry):
- scope: t.Literal["clear_graph"]
- policy: t.Literal["allow", "deny", "abstain"]
- role: str
- graph: t.Literal["*", "named", "default"] | URIRef
- def __post_init__(self) -> None:
- self.policy = _parse_policy(self.policy)
- self.role = _parse_role(self.role)
- self.graph = _parse_graph(self.graph)
- def as_dict(self) -> dict[str, t.Any]:
- return {
- "scope": self.scope,
- "policy": self.policy,
- "role": self.role,
- "context": _format_term(self.graph),
- }
- _ALLOWED_POLICIES = {"allow", "deny", "abstain"}
- _ALLOWED_OPERATIONS = {"read", "write", "*"}
- _ALLOWED_GRAPHS = {"*", "named", "default"}
- def _parse_policy(policy: t.Any) -> t.Literal["allow", "deny", "abstain"]:
- if policy not in _ALLOWED_POLICIES:
- raise ValueError(f"Invalid FGAC policy: {policy!r}")
- return t.cast(t.Literal["allow", "deny", "abstain"], policy)
- def _parse_operation(operation: t.Any) -> t.Literal["read", "write", "*"]:
- if operation not in _ALLOWED_OPERATIONS:
- raise ValueError(f"Invalid FGAC operation: {operation!r}")
- return t.cast(t.Literal["read", "write", "*"], operation)
- def _parse_role(role: t.Any) -> str:
- if not isinstance(role, str):
- raise ValueError(f"Invalid FGAC role: {role!r}")
- return role
- def _parse_plugin(plugin: t.Any) -> str:
- if not isinstance(plugin, str):
- raise ValueError(f"Invalid FGAC plugin: {plugin!r}")
- return plugin
- def _parse_subject(subject: t.Any) -> t.Literal["*"] | URIRef:
- if subject == "*":
- return "*"
- if isinstance(subject, URIRef):
- return subject
- if isinstance(subject, str):
- parsed = _parse_with_n3(subject)
- if isinstance(parsed, URIRef):
- return parsed
- raise ValueError(f"Invalid FGAC subject: {subject!r}")
- def _parse_predicate(predicate: t.Any) -> t.Literal["*"] | URIRef:
- if predicate == "*":
- return "*"
- if isinstance(predicate, URIRef):
- return predicate
- if isinstance(predicate, str):
- parsed = _parse_with_n3(predicate)
- if isinstance(parsed, URIRef):
- return parsed
- raise ValueError(f"Invalid FGAC predicate: {predicate!r}")
- def _parse_object(obj: t.Any) -> t.Literal["*"] | URIRef | Literal:
- if obj == "*":
- return "*"
- if isinstance(obj, (URIRef, Literal)):
- return obj
- if isinstance(obj, str):
- parsed = _parse_with_n3(obj)
- if isinstance(parsed, (URIRef, Literal)):
- return parsed
- raise ValueError(f"Invalid FGAC object: {obj!r}")
- def _parse_graph(graph: t.Any) -> t.Literal["*", "named", "default"] | URIRef:
- if graph in _ALLOWED_GRAPHS:
- return t.cast(t.Literal["*", "named", "default"], graph)
- if isinstance(graph, URIRef):
- return graph
- if isinstance(graph, str):
- parsed = _parse_with_n3(graph)
- if isinstance(parsed, URIRef):
- return parsed
- raise ValueError(f"Invalid FGAC graph: {graph!r}")
- def _parse_with_n3(value: str) -> URIRef | Literal:
- try:
- parsed = from_n3(value)
- except Exception:
- parsed = None
- if isinstance(parsed, (URIRef, Literal)):
- return parsed
- try:
- return URIRef(value)
- except Exception as err: # pragma: no cover - defensive
- raise ValueError(f"Unable to parse value {value!r} into an RDF term.") from err
- def _format_term(value: t.Any) -> str:
- if isinstance(value, (URIRef, Literal)):
- return value.n3()
- return t.cast(str, value)
|