diff --git a/app/lib/dartdoc/models.dart b/app/lib/dartdoc/models.dart index 27f8dec7b..78caf9f8c 100644 --- a/app/lib/dartdoc/models.dart +++ b/app/lib/dartdoc/models.dart @@ -7,7 +7,7 @@ import 'package:json_annotation/json_annotation.dart'; part 'models.g.dart'; /// Describes the resolved version and the URL redirect info. -@JsonSerializable() +@JsonSerializable(includeIfNull: false) class ResolvedDocUrlVersion { /// The version to use to display the documentation. final String version; @@ -20,13 +20,18 @@ class ResolvedDocUrlVersion { /// * `""` (indicating empty response) final String urlSegment; + /// When the resolution is empty, the 404 response will have this message. + final String? message; + ResolvedDocUrlVersion({ required this.version, required this.urlSegment, + this.message, }); - ResolvedDocUrlVersion.empty() - : version = '', + ResolvedDocUrlVersion.empty({ + required this.message, + }) : version = '', urlSegment = ''; factory ResolvedDocUrlVersion.fromJson(Map json) => @@ -35,4 +40,5 @@ class ResolvedDocUrlVersion { Map toJson() => _$ResolvedDocUrlVersionToJson(this); bool get isEmpty => version.isEmpty || urlSegment.isEmpty; + bool get isLatestStable => urlSegment == 'latest'; } diff --git a/app/lib/dartdoc/models.g.dart b/app/lib/dartdoc/models.g.dart index fd5e61283..8bcf53a28 100644 --- a/app/lib/dartdoc/models.g.dart +++ b/app/lib/dartdoc/models.g.dart @@ -11,11 +11,22 @@ ResolvedDocUrlVersion _$ResolvedDocUrlVersionFromJson( ResolvedDocUrlVersion( version: json['version'] as String, urlSegment: json['urlSegment'] as String, + message: json['message'] as String?, ); Map _$ResolvedDocUrlVersionToJson( - ResolvedDocUrlVersion instance) => - { - 'version': instance.version, - 'urlSegment': instance.urlSegment, - }; + ResolvedDocUrlVersion instance) { + final val = { + 'version': instance.version, + 'urlSegment': instance.urlSegment, + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('message', instance.message); + return val; +} diff --git a/app/lib/frontend/handlers/documentation.dart b/app/lib/frontend/handlers/documentation.dart index fcff034d9..67e8f7052 100644 --- a/app/lib/frontend/handlers/documentation.dart +++ b/app/lib/frontend/handlers/documentation.dart @@ -50,7 +50,10 @@ Future documentationHandler(shelf.Request request) async { final version = docFilePath.version!; final resolved = await _resolveDocUrlVersion(package, version); if (resolved.isEmpty) { - return notFoundHandler(request); + return notFoundHandler( + request, + body: resolved.message ?? default404NotFound, + ); } if (version != resolved.urlSegment) { return redirectResponse(pkgDocUrl( @@ -147,7 +150,8 @@ Future _resolveDocUrlVersion( if (version == 'latest') { final latestFinished = await taskBackend.latestFinishedVersion(package); if (latestFinished == null) { - return ResolvedDocUrlVersion.empty(); + return ResolvedDocUrlVersion.empty( + message: 'Analysis has not started yet.'); } final latestVersion = await packageBackend.getLatestVersion(package); return ResolvedDocUrlVersion( @@ -159,7 +163,7 @@ Future _resolveDocUrlVersion( // Do not resolve if package version does not exists. final pv = await packageBackend.lookupPackageVersion(package, version); if (pv == null) { - return ResolvedDocUrlVersion.empty(); + return ResolvedDocUrlVersion.empty(message: 'Not found.'); } // Select the closest version (may be the same as version) that has a finished analysis. diff --git a/app/lib/task/handlers.dart b/app/lib/task/handlers.dart index 5949bbaf1..c4e5dc54e 100644 --- a/app/lib/task/handlers.dart +++ b/app/lib/task/handlers.dart @@ -9,6 +9,7 @@ import 'package:pub_dev/shared/handlers.dart'; import 'package:pub_dev/shared/redis_cache.dart'; import 'package:pub_dev/shared/urls.dart'; import 'package:pub_dev/task/backend.dart'; +import 'package:pub_dev/task/models.dart'; import 'package:shelf/shelf.dart' as shelf; const _safeMimeTypes = { @@ -111,7 +112,31 @@ Future handleDartDoc( }); // We use empty string to indicate missing file or bug in the file if (htmlBytes == null || htmlBytes.isEmpty) { - return notFoundHandler(request); + final status = await taskBackend.packageStatus(package); + final vs = status.versions[version]; + if (vs == null) { + return notFoundHandler( + request, + body: resolvedDocUrlVersion.isLatestStable + ? 'Analysis has not started yet.' + : 'Version not selected for analysis.', + ); + } + String? message; + switch (vs.status) { + case PackageVersionStatus.pending: + case PackageVersionStatus.running: + message = 'Analysis has not finished yet.'; + break; + case PackageVersionStatus.failed: + message = + 'Analysis has failed, no `dartdoc` output has been generated.'; + break; + case PackageVersionStatus.completed: + message = '`dartdoc` did not generate this page.'; + break; + } + return notFoundHandler(request, body: message); } return htmlBytesResponse(htmlBytes); }