Files
strukto-ai--mirage/integ/onedrive_server.py
T
wehub-resource-sync bcbd1bdb22
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:44 +08:00

413 lines
15 KiB
Python

# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import posixpath
from collections import Counter
from datetime import datetime, timezone
from aiohttp import web
import mirage.core.onedrive._client as onedrive_client
MODIFIED = datetime(2026, 3, 31,
tzinfo=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
def _norm(path: str) -> str:
return path.strip("/")
def _parse_range(header: str | None, size: int) -> tuple[int, int]:
if not header or not header.startswith("bytes="):
return 0, size
spec = header[len("bytes="):]
start_s, _, end_s = spec.partition("-")
start = int(start_s) if start_s else 0
end = int(end_s) + 1 if end_s else size
return start, min(end, size)
class FakeGraph:
def __init__(self) -> None:
self.files: dict[str, dict] = {}
self.dirs: set[str] = {""}
self.base = ""
self._seq = 0
def _tag(self) -> str:
self._seq += 1
return f"tag{self._seq}"
def _ensure_parents(self, path: str) -> None:
parent = posixpath.dirname(path)
while parent:
self.dirs.add(parent)
parent = posixpath.dirname(parent)
def _write_file(self, path: str, content: bytes) -> dict:
path = _norm(path)
self._ensure_parents(path)
prior = self.files.get(path)
versions = prior["versions"] if prior else []
ctag = self._tag()
versions = versions + [{
"id": ctag,
"lastModifiedDateTime": MODIFIED,
"content": content,
}]
entry = {
"content": content,
"ctag": ctag,
"etag": ctag,
"modified": MODIFIED,
"versions": versions,
}
self.files[path] = entry
return self._file_item(path)
def _children(self, dirpath: str) -> list[str]:
dirpath = _norm(dirpath)
names: set[str] = set()
for f in self.files:
if posixpath.dirname(f) == dirpath:
names.add(posixpath.basename(f))
for d in self.dirs:
if d and posixpath.dirname(d) == dirpath:
names.add(posixpath.basename(d))
return sorted(names)
def _file_item(self, path: str) -> dict:
entry = self.files[path]
return {
"id":
entry["ctag"],
"name":
posixpath.basename(path),
"size":
len(entry["content"]),
"lastModifiedDateTime":
entry["modified"],
"cTag":
entry["ctag"],
"eTag":
entry["etag"],
"file": {
"mimeType": "application/octet-stream"
},
"@microsoft.graph.downloadUrl":
f"{self.base}/download/{path}",
"versions": [{
"id": v["id"],
"lastModifiedDateTime": v["lastModifiedDateTime"],
} for v in entry["versions"]],
}
def _folder_size(self, path: str) -> int:
path = _norm(path)
prefix = path + "/" if path else ""
return sum(
len(entry["content"]) for f, entry in self.files.items()
if not path or f == path or f.startswith(prefix))
def _folder_item(self, path: str) -> dict:
return {
"id": f"folder:{path}" if path else "root",
"name": posixpath.basename(path) if path else "root",
"size": self._folder_size(path),
"lastModifiedDateTime": MODIFIED,
"folder": {
"childCount": len(self._children(path))
},
}
def _item(self, path: str) -> dict | None:
path = _norm(path)
if path in self.files:
return self._file_item(path)
if path in self.dirs:
return self._folder_item(path)
return None
def _delete(self, path: str) -> bool:
path = _norm(path)
if path in self.files:
del self.files[path]
return True
if path in self.dirs:
self.dirs.discard(path)
for f in list(self.files):
if f == path or f.startswith(path + "/"):
del self.files[f]
for d in list(self.dirs):
if d == path or d.startswith(path + "/"):
self.dirs.discard(d)
return True
return False
def _not_found() -> web.Response:
return web.json_response(
{"error": {
"code": "itemNotFound",
"message": "Item not found"
}},
status=404)
def _parse_item_path(path: str) -> tuple[str, str]:
idx = path.find("/root")
if idx < 0:
return "", ""
rest = path[idx + len("/root"):]
if rest in ("", "/"):
return "", ""
if rest == "/children":
return "", "children"
rest = rest[1:] if rest.startswith(":") else rest
item_part, sep, action = rest.partition(":/")
return _norm(item_part), (action if sep else "")
def _ref_parent(ref_path: str) -> str:
after = ref_path.split("root", 1)[-1]
after = after[1:] if after.startswith(":") else after
return _norm(after)
class GraphServer:
def __init__(self, state: FakeGraph) -> None:
self.state = state
self.uploads: dict[str, dict] = {}
self.calls: Counter = Counter()
self._upload_seq = 0
async def handle(self, request: web.Request) -> web.StreamResponse:
path = request.path
method = request.method
if path.startswith("/download/"):
self.calls["download"] += 1
return self._serve_bytes(request, path[len("/download/"):])
if path.startswith("/upload/"):
return await self._upload(request, path[len("/upload/"):])
if path.startswith("/monitor/"):
return web.json_response({"status": "completed"})
item_path, action = _parse_item_path(path)
if method == "GET":
kind = action if action in ("children", "content") else "item"
self.calls[kind] += 1
return await self._drive(request, method, item_path, action)
async def _drive(self, request: web.Request, method: str, item_path: str,
action: str) -> web.StreamResponse:
state = self.state
if action == "children":
if method == "POST":
return await self._mkdir(request, item_path)
return self._children_response(item_path)
if action == "content":
if method == "PUT":
data = await request.read()
return web.json_response(state._write_file(item_path, data))
return self._content_response(request, item_path)
if action == "createUploadSession":
return self._create_upload(item_path)
if action == "copy":
return await self._copy(request, item_path)
if action.startswith("versions/") and action.endswith("/content"):
vid = action[len("versions/"):-len("/content")]
return self._version_content(request, item_path, vid)
if action.endswith("/restoreVersion"):
return web.Response(status=204)
if action == "versions":
return self._versions_response(item_path)
if method == "DELETE":
return web.Response(
status=204) if state._delete(item_path) else _not_found()
if method == "PATCH":
return await self._patch(request, item_path)
item = state._item(item_path)
return web.json_response(item) if item is not None else _not_found()
def _children_response(self, item_path: str) -> web.Response:
state = self.state
if item_path and item_path not in state.dirs:
return _not_found()
value = [
state._item(posixpath.join(item_path, name))
for name in state._children(item_path)
]
return web.json_response({"value": value})
def _content_response(self, request: web.Request,
item_path: str) -> web.Response:
return self._serve_bytes(request, item_path)
def _serve_bytes(self, request: web.Request, path: str) -> web.Response:
entry = self.state.files.get(_norm(path))
if entry is None:
return _not_found()
return self._range_body(request, entry["content"])
def _range_body(self, request: web.Request,
content: bytes) -> web.Response:
header = request.headers.get("Range")
start, end = _parse_range(header, len(content))
body = content[start:end]
if header and header.startswith("bytes=") and start < end:
return web.Response(status=206,
body=body,
content_type="application/octet-stream",
headers={
"Content-Range":
f"bytes {start}-{end - 1}/{len(content)}"
})
return web.Response(body=body, content_type="application/octet-stream")
def _versions_response(self, item_path: str) -> web.Response:
entry = self.state.files.get(_norm(item_path))
if entry is None:
return _not_found()
value = [{
"id": v["id"],
"lastModifiedDateTime": v["lastModifiedDateTime"],
} for v in entry["versions"]]
return web.json_response({"value": value})
def _version_content(self, request: web.Request, item_path: str,
vid: str) -> web.Response:
entry = self.state.files.get(_norm(item_path))
if entry is None:
return _not_found()
for v in entry["versions"]:
if v["id"] == vid:
return self._range_body(request, v["content"])
return _not_found()
async def _mkdir(self, request: web.Request, parent: str) -> web.Response:
body = await request.json()
name = body.get("name", "")
target = _norm(posixpath.join(parent, name))
self.state._ensure_parents(target)
self.state.dirs.add(target)
return web.json_response(self.state._folder_item(target))
async def _patch(self, request: web.Request,
item_path: str) -> web.Response:
state = self.state
item_path = _norm(item_path)
body = await request.json()
name = body.get("name") or posixpath.basename(item_path)
ref = body.get("parentReference", {})
if "path" in ref:
parent = _ref_parent(ref["path"])
else:
parent = posixpath.dirname(item_path)
dest = _norm(posixpath.join(parent, name))
if item_path in state.files:
entry = state.files.pop(item_path)
state._ensure_parents(dest)
state.files[dest] = entry
return web.json_response(state._file_item(dest))
if item_path in state.dirs:
self._move_dir(item_path, dest)
return web.json_response(state._folder_item(dest))
return _not_found()
def _move_dir(self, src: str, dest: str) -> None:
state = self.state
state.dirs.discard(src)
state.dirs.add(dest)
state._ensure_parents(dest)
for f in list(state.files):
if f == src or f.startswith(src + "/"):
entry = state.files.pop(f)
state.files[dest + f[len(src):]] = entry
for d in list(state.dirs):
if d != src and d.startswith(src + "/"):
state.dirs.discard(d)
state.dirs.add(dest + d[len(src):])
async def _copy(self, request: web.Request,
item_path: str) -> web.Response:
state = self.state
item_path = _norm(item_path)
body = await request.json()
name = body.get("name") or posixpath.basename(item_path)
parent = _ref_parent(body.get("parentReference", {}).get("path", ""))
dest = _norm(posixpath.join(parent, name))
if item_path in state.files:
state._write_file(dest, state.files[item_path]["content"])
elif item_path in state.dirs:
self._copy_dir(item_path, dest)
else:
return _not_found()
resp = web.Response(status=202)
resp.headers["Location"] = f"{state.base}/monitor/{dest}"
return resp
def _copy_dir(self, src: str, dest: str) -> None:
state = self.state
state.dirs.add(dest)
state._ensure_parents(dest)
for f in list(state.files):
if f == src or f.startswith(src + "/"):
state._write_file(dest + f[len(src):],
state.files[f]["content"])
for d in list(state.dirs):
if d != src and d.startswith(src + "/"):
state.dirs.add(dest + d[len(src):])
def _create_upload(self, item_path: str) -> web.Response:
self._upload_seq += 1
token = f"{_norm(item_path)}#{self._upload_seq}"
self.uploads[token] = {"path": _norm(item_path), "buffer": bytearray()}
return web.json_response({
"uploadUrl": f"{self.state.base}/upload/{token}",
"expirationDateTime": MODIFIED,
})
async def _upload(self, request: web.Request, token: str) -> web.Response:
session = self.uploads.get(token)
if session is None:
return _not_found()
chunk = await request.read()
session["buffer"].extend(chunk)
content_range = request.headers.get("Content-Range", "")
total = int(content_range.rsplit("/", 1)[-1]) if "/" in content_range \
else len(session["buffer"])
if len(session["buffer"]) >= total:
item = self.state._write_file(session["path"],
bytes(session["buffer"]))
del self.uploads[token]
return web.json_response(item, status=201)
return web.json_response(
{"nextExpectedRanges": [f"{len(session['buffer'])}-"]}, status=202)
async def start_fake_graph() -> tuple[FakeGraph, "GraphServer", web.AppRunner]:
state = FakeGraph()
server = GraphServer(state)
app = web.Application()
app.router.add_route("*", "/{tail:.*}", server.handle)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, "127.0.0.1", 0)
await site.start()
port = site._server.sockets[0].getsockname()[1]
state.base = f"http://127.0.0.1:{port}"
onedrive_client.GRAPH_API = state.base
return state, server, runner