diff --git a/migas/server/database.py b/migas/server/database.py index 8994d6d..bfaa3e3 100644 --- a/migas/server/database.py +++ b/migas/server/database.py @@ -92,8 +92,8 @@ async def ingest_project(project: Project) -> None: language=data['language'], language_version=data['language_version'], timestamp=data['timestamp'], - session_id=data['session_id'], user_id=data['context']['user_id'], + session_id=data['context']['session_id'], status=data['process']['status'], status_desc=data['process']['status_desc'], error_type=data['process']['error_type'], diff --git a/migas/server/schema.py b/migas/server/schema.py index dafc099..ca57836 100644 --- a/migas/server/schema.py +++ b/migas/server/schema.py @@ -23,8 +23,10 @@ from .types import ( AuthenticationResult, Context, + ContextInput, DateTime, Process, + ProcessInput, Project, ProjectInput, ) @@ -39,12 +41,24 @@ async def get_projects(self) -> list[str]: projs = await query_projects() return projs + @strawberry.field + async def check_project(self, project: str, project_version: str | None = None) -> JSON: + '''Check project for latest version, developer notes, etc.''' + fetched = await fetch_project_info(project) + return { + 'bad_versions': fetched['bad_versions'], + 'cached': fetched['cached'], + 'latest_version': fetched['version'], + 'message': '', # TODO: Allow message for bad_versions + 'success': fetched['success'], + } + @strawberry.field async def get_usage( self, project: str, start: DateTime, - end: DateTime = None, + end: DateTime | None = None, unique: bool = False, ) -> JSON: ''' @@ -76,14 +90,16 @@ async def get_usage( } @strawberry.field - async def login(token: str) -> AuthenticationResult: + async def login(self, token: str) -> AuthenticationResult: valid, projects = await verify_token(token) if not valid: + success = False msg = 'Authentication Error: token is either invalid or expired.' else: + success = True msg = 'Authentication successful.' return AuthenticationResult( - token=token, + success=success, projects=projects, message=msg, ) @@ -99,6 +115,49 @@ async def usage_stats(self, project: str, token: str) -> JSON: @strawberry.type class Mutation: + @strawberry.field + async def add_breadcrumb( + self, + info: Info, + project: str, + project_version: str, + language: str, + language_version: str, + ctx: ContextInput, + proc: ProcessInput, + ) -> bool: + if not '/' in project: + return False + + context = Context( + user_id=ctx.user_id, + session_id=ctx.session_id, + platform=ctx.platform, + container=ctx.container, + is_ci=ctx.is_ci, + ) + process = Process( + status=proc.status, + status_desc=proc.status_desc, + error_type=proc.error_type, + error_desc=proc.error_desc, + ) + + project = Project( + project=project, + project_version=project_version, + language=language, + language_version=language_version, + timestamp=now(), + context=context, + process=process, + ) + + bg_tasks = info.context['background_tasks'] + bg_tasks.add_task(ingest_project, project) + return True + + @strawberry.field async def add_project(self, p: ProjectInput, info: Info) -> JSON: # validate project @@ -111,10 +170,10 @@ async def add_project(self, p: ProjectInput, info: Info) -> JSON: project_version=p.project_version, language=p.language, language_version=p.language_version, - session_id=p.session_id, timestamp=now(), context=Context( user_id=p.user_id, + session_id=p.session_id, user_type=p.user_type, platform=p.platform, container=p.container, @@ -131,7 +190,6 @@ async def add_project(self, p: ProjectInput, info: Info) -> JSON: fetched = await fetch_project_info(p.project) # return project info ASAP, assign data ingestion as background tasks - request = info.context['request'] bg_tasks = info.context['background_tasks'] bg_tasks.add_task(ingest_project, project) diff --git a/migas/server/types.py b/migas/server/types.py index 05f12db..35ec054 100644 --- a/migas/server/types.py +++ b/migas/server/types.py @@ -35,20 +35,6 @@ parse_value=parse_version, ) -# Arguments = scalar( -# str, -# name="Argument", -# description="Argument/Value pairs", -# serialize=json.dumps, -# parse_value=json.loads -# ) -Arguments = scalar( - typing.NewType("JSONScalar", typing.Any), - serialize=lambda v: json.dumps(v), - parse_value=lambda v: json.loads(v), - parse_literal=value_from_ast_untyped, -) - @strawberry.enum class Container(Enum): @@ -103,6 +89,7 @@ class Process: @strawberry.type class Context: user_id: str | None = None + session_id: str | None = None user_type: User = User.general platform: str = "unknown" container: Container = Container.unknown @@ -117,9 +104,35 @@ class Project: language_version: Version timestamp: DateTime # optional - session_id: UUID | None = None - context: Context = None - process: Process = Process + context: Context + process: Process + + +@strawberry.input +class ContextInput: + session_id: str | None = strawberry.field( + description="Unique identifier for telemetry session", default=None + ) + user_id: str | None = strawberry.field(description="Unique identifier for migas client", default=None) + user_type: User = strawberry.field( + description="Identifier of user role", default=User.general + ) + platform: str = strawberry.field(description="Client platform type", default="unknown") + container: Container = strawberry.field( + description="Check if client pings from inside a container", default=Container.unknown + ) + is_ci: bool = strawberry.field( + description="Client is pinging from continous integration", default=False + ) + +@strawberry.input +class ProcessInput: + status: Status = strawberry.field( + description="For timeseries pings, the current process status", default=Status.R + ) + status_desc: str | None = strawberry.field(description="Description of status ping", default=None) + error_type: str | None = strawberry.field(description="Type of error encountered", default=None) + error_desc: str | None = strawberry.field(description="Description of error", default=None) @strawberry.input @@ -129,28 +142,28 @@ class ProjectInput: language: str = strawberry.field(description="Programming language of project") language_version: Version = strawberry.field(description="Programming language version") # optional - session_id: str = strawberry.field( + session_id: str | None = strawberry.field( description="Unique identifier for telemetry session", default=None ) # context args - user_id: str = strawberry.field(description="Unique identifier for migas client", default=None) - user_type: 'User' = strawberry.field( + user_id: str | None = strawberry.field(description="Unique identifier for migas client", default=None) + user_type: User = strawberry.field( description="Identifier of user role", default=User.general ) - platform: str = strawberry.field(description="Client platform type", default=None) - container: 'Container' = strawberry.field( + platform: str = strawberry.field(description="Client platform type", default="unknown") + container: Container = strawberry.field( description="Check if client pings from inside a container", default=Container.unknown ) is_ci: bool = strawberry.field( description="Client is pinging from continous integration", default=False ) # process args - status: 'Status' = strawberry.field( + status: Status = strawberry.field( description="For timeseries pings, the current process status", default=Status.R ) - status_desc: str = strawberry.field(description="Description of status ping", default=None) - error_type: str = strawberry.field(description="Type of error encountered", default=None) - error_desc: str = strawberry.field(description="Description of error", default=None) + status_desc: str | None = strawberry.field(description="Description of status ping", default=None) + error_type: str | None = strawberry.field(description="Type of error encountered", default=None) + error_desc: str | None = strawberry.field(description="Description of error", default=None) # arguments: Arguments = strawberry.field( # description="Client side arguments used", default_factory=lambda: "{}" # ) @@ -158,7 +171,7 @@ class ProjectInput: @strawberry.type class AuthenticationResult: - token: str + success: bool projects: typing.List[str] message: str diff --git a/migas/static/viz.html b/migas/static/viz.html index 12f435b..8c6983a 100644 --- a/migas/static/viz.html +++ b/migas/static/viz.html @@ -42,13 +42,13 @@ 'Content-Type': 'application/json', }, body: JSON.stringify({ - query: `{ login(token: "${token}") { token projects message } }` + query: `{ login(token: "${token}") { success projects message } }` }) }) .then((res) => res.json()) .then((res) => { auth = res.data.login - if (auth.projects.length == 0) { + if (!auth.success || !auth.projects.length) { alert(auth.message) return }