A clean domain layer is what makes Xolo’s architecture genuinely maintainable. By housing the data-access contract, all entity definitions, and the business-logic services in a package that has zero dependency on Flutter widgets, Drift, or Riverpod, every piece of behaviour can be unit-tested in isolation against a plain mock. Adding a new feature begins with the domain — extend the contract, write a test, then wire up the database implementation — rather than the other way around.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/JonathanHerSa/xolo-api-hub/llms.txt
Use this file to discover all available pages before exploring further.
XoloRepository Interface
XoloRepository is the single gateway between the presentation layer and all persistent data. It lives in lib/domain/repositories/xolo_repository.dart and contains no imports from package:drift. Every widget, provider, and service depends only on this abstract class, which means the underlying storage engine can be swapped without touching a single screen.
- Settings
- Collections
- Saved Requests
- History
- Environments & Variables
- Collection Runs
- Data Management
Simple key-value configuration store.
watchSetting emits reactively whenever the stored value changes.Domain Entities
All domain entities are plain Dart classes withconst constructors. They carry no Drift annotations, no Flutter imports, and no JSON serialisation logic (that lives in entity_mappers.dart). This makes them trivial to construct in tests and safe to pass across isolates.
CollectionEntity
Represents a collection node (root workspace or nested folder).Key fields:
id, name, description (nullable), parentId (nullable FK — null means root), authType, authData, createdAt.Helper: bool get isRoot => parentId == null;SavedRequestEntity
A reusable HTTP request with optional collection membership, auth override, schema hint, scripts, and assertions.Key fields:
id, name, method, url, headersJson, paramsJson, body, collectionId (nullable), authType, authData, schemaJson, preScriptsJson, scriptsJson, assertionsJson, createdAt, updatedAt, isDeleted.EnvironmentEntity
A named variable scope belonging to a workspace collection.Key fields:
id, name, collectionId (nullable — null = user-global), isActive, createdAt.EnvVariableEntity
A single variable key-value pair.
scope drives resolution priority: 'env' entries override 'global' entries with the same key.Key fields: id, key, value, environmentId (nullable), collectionId (nullable), scope ('global' or 'env'), createdAt.HistoryEntryEntity
An immutable snapshot of one HTTP execution, including auth credentials at the time of the call for replay fidelity.Key fields:
id, savedRequestId (nullable), workspaceId (nullable), method, url, originalUrl (nullable template), headersJson, paramsJson, body, authType, authData, statusCode, responseBody, durationMs, executedAt.AssertionRuleEntity
A single declarative validation rule attached to a
SavedRequestEntity. Rules are serialised to assertionsJson and evaluated by AssertionEvaluator.Key fields: type (AssertionType enum), target (nullable — JSONPath or field name), expected (expected value/threshold), operator (defaults 'equals').AssertionType values: statusCode, responseTimeMs, jsonPathExists, jsonPathEquals, bodyContains.CollectionRunEntity
Header record for one automated collection run.
allPassed is a convenience getter used by the UI to decide which badge colour to render.Key fields: id, collectionId, workspaceId (nullable), environmentId (nullable), status (RunStatus enum), totalSteps, passedSteps, failedSteps, skippedSteps, startedAt, finishedAt (nullable), stopOnFailure, variablesSnapshotJson.RunStatus values: running, completed, failed, cancelled.RunStepResultEntity
Per-step execution record.
assertionResults is a List<AssertionResultEntity> deserialised from the JSON column by the mapper.Key fields: stepIndex, savedRequestId (nullable), name, method, url, status (RunStepStatus enum), statusCode, durationMs, passed, assertionResults, errorMessage, responseBodySnippet, extractedVariables.RunStepStatus values: passed, failed, skipped, error.RunPlanItem and RunOptions
RunPlanItem is the atomic unit of a collection run plan. It wraps a SavedRequestEntity with its zero-based stepIndex and the owning collectionId, giving CollectionRunnerService everything it needs to dispatch the request.
RunOptions controls run-time behaviour and is serialised to runOptionsJson in CollectionRuns so past runs can be replayed exactly:
Pure Domain Services
The three service classes inlib/domain/services/ contain all the execution intelligence. They depend only on XoloRepository and standard Dart libraries — no Flutter, no Drift, no Riverpod — making them fully unit-testable with a simple mock repository.
RunPlanBuilder — depth-first tree flattening
RunPlanBuilder — depth-first tree flattening
RunPlanBuilder converts a collection tree into a flat, ordered List<RunPlanItem>. The algorithm is a depth-first walk: for each node it first enqueues that node’s direct requests (in DB order), then recurses into each sub-collection.Inputs: collectionId (root of the tree to run), XoloRepository (injected at construction).Output: Future<List<RunPlanItem>> — items are numbered sequentially from index 0.RunPlanBuilder only calls fetchRequestsInCollection and fetchSubCollections, it can be tested with a two-method mock without touching a real database.CollectionRunnerService — sequential run orchestration
CollectionRunnerService — sequential run orchestration
CollectionRunnerService drives the full execution lifecycle. It is constructed with three dependencies — RequestPipeline (HTTP), AuthResolverService (auth), and XoloRepository (persistence) — and exposes a single execute method.The service steps through the plan sequentially:Create run record
Calls
repository.createCollectionRun(...) to open a CollectionRuns row with status running.Skip check
If
RunOptions.skipIfVariableEmpty lists a variable that is absent or empty in the current workingVars map, the step is marked skipped and an explanatory message is stored as errorMessage.Pre-scripts
ScriptExecutor.executePreScripts evaluates preScriptsJson rules, adding any extracted values to workingVars before the request is sent.HTTP dispatch
RequestPipeline.send(...) fires the request with resolved headers, query params, body, and auth. If the CancelToken is cancelled mid-run, execution stops and status becomes cancelled.Assertion evaluation
AssertionEvaluator.evaluate(...) is called with the response data and the deserialized List<AssertionRuleEntity>. The overall stepPassed flag is true only when output.error == null and every assertion passes.Post-scripts (variable chaining)
scriptsJson rules are applied with JSONPath to extract values from the response body and write them back to workingVars and the repository via upsertVariable. These variables are then available to subsequent steps.Persist step result
A
RunStepResultEntity is constructed, emitted via the onStep callback (for live UI updates), and persisted via repository.insertRunStepResult(...).CollectionRunnerService has no import 'package:flutter/...' — it can run in any Dart environment, including unit tests and background isolates.AssertionEvaluator — pure response validation
AssertionEvaluator — pure response validation
AssertionEvaluator is a static-only class (private constructor) that maps a list of AssertionRuleEntity rules against a response and returns a List<AssertionResultEntity>. It has no mutable state and no I/O.Inputs: rules, statusCode, durationMs, responseData (parsed JSON or string), errorMessage.Output: List<AssertionResultEntity> — one result per rule. If rules is empty, a single implicit passed: true result is returned so the step is not penalised.AssertionType | Behaviour |
|---|---|
statusCode | Exact match (equals) or membership check (in operator with comma-separated list) |
responseTimeMs | durationMs < limit (lessThan) or durationMs <= limit |
jsonPathExists | Verifies at least one non-null match at the given JSONPath |
jsonPathEquals | Compares the first JSONPath match (as string) against the expected value (quotes stripped) |
bodyContains | String.contains check on the serialised response body |
AssertionEvaluator is a pure function with no side effects, testing it requires nothing more than calling evaluate(...) and asserting on the returned list.