Skip to content

Commit

Permalink
Merge #131
Browse files Browse the repository at this point in the history
131: feat: fixed for the issue (#126) r=myConsciousness a=myConsciousness

# 1. Description

<!-- Provide a description of what this PR is doing.
If you're modifying existing behavior, describe the existing behavior, how this PR is changing it,
and what motivated the change. If this is a breaking change, specify explicitly which APIs have been
changed. -->

## 1.1. Checklist

<!-- Before you create this PR confirm that it meets all requirements listed below by checking the
relevant checkboxes (`[x]`). This will ensure a smooth and quick review process. -->

- [x] The title of my PR starts with a [Conventional Commit] prefix (`fix:`, `feat:`, `docs:` etc).
- [x] I have read the [Contributor Guide] and followed the process outlined for submitting PRs.
- [x] I have updated/added tests for ALL new/updated/fixed functionality.
- [x] I have updated/added relevant documentation in `docs` and added dartdoc comments with `///`.
- [x] I have updated/added relevant examples in `examples`.

## 1.2. Breaking Change

<!-- Does your PR require users to manually update their apps to accommodate your change?

If the PR is a breaking change this should be indicated with suffix "!"  (for example, `feat!:`, `fix!:`). See [Conventional Commit] for details.
-->

- [ ] Yes, this is a breaking change.
- [x] No, this is _not_ a breaking change.

## 1.3. Related Issues

<!-- Provide a list of issues related to this PR from the [issue database].
Indicate which of these issues are resolved or fixed by this PR, i.e. Fixes #xxxx* !-->

<!-- Links -->

[issue database]: https://github.com/mastodon-dart/mastodon-api/issues
[contributor guide]: https://github.com/mastodon-dart/mastodon-api/blob/main/CONTRIBUTING.md
[style guide]: https://github.com/mastodon-dart/mastodon-api/blob/main/STYLEGUIDE.md
[conventional commit]: https://conventionalcommits.org


Co-authored-by: myConsciousness <contact@shinyakato.dev>
  • Loading branch information
bors[bot] and myConsciousness committed Feb 6, 2023
2 parents ea9e09b + 2eef008 commit c2b8e49
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 1 deletion.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
- `GET /api/v1/domain_blocks`
- `POST /api/v1/domain_blocks`
- `DELETE /api/v1/domain_blocks`
- Add response headers in `MastodonResponse`. ([#128](https://github.com/mastodon-dart/mastodon-api/issues/128))
- Added response headers in `MastodonResponse`. ([#128](https://github.com/mastodon-dart/mastodon-api/issues/128))
- Added HTTP status in `MastodonResponse`. ([#126](https://github.com/mastodon-dart/mastodon-api/issues/126))

## v0.4.0

Expand Down
1 change: 1 addition & 0 deletions lib/mastodon_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export 'package:mastodon_api/src/core/exception/data_not_found_exception.dart';
export 'package:mastodon_api/src/core/exception/mastodon_exception.dart';
export 'package:mastodon_api/src/core/exception/rate_limit_exceeded_exception.dart';
export 'package:mastodon_api/src/core/exception/unauthorized_exception.dart';
export 'package:mastodon_api/src/core/http_status.dart';
export 'package:mastodon_api/src/core/language.dart';
export 'package:mastodon_api/src/core/locale.dart';
export 'package:mastodon_api/src/core/mime_type.dart';
Expand Down
163 changes: 163 additions & 0 deletions lib/src/core/http_status.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright 2023 Kato Shinya. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided the conditions.

enum HttpStatus {
/// The request has succeeded. The meaning of a success varies
/// depending on the HTTP method:
///
/// GET: The resource has been fetched and is transmitted in the message body.
/// HEAD: The entity headers are in the message body.
/// POST: The resource describing the result of the action is transmitted in
/// the message body.
///
/// TRACE: The message body contains the request message as received by the
/// server
///
/// https://tools.ietf.org/html/rfc7231#section-6.3.1
ok(200, 'OK'),

/// The request has succeeded and a new resource has been created
/// as a result of it. This is typically the response sent
/// after a PUT request.
///
/// https://tools.ietf.org/html/rfc7231#section-6.3.2
created(201, 'Created'),

/// The request has been received but not yet acted upon.
/// It is noncommittal, since there is no way in HTTP to later send
/// an asynchronous response indicating the outcome of the request.
///
/// It is intended for cases where another process or server handles t
/// he request, or for batch processing.
///
/// https://tools.ietf.org/html/rfc7231#section-6.3.3
accepted(202, 'Accepted'),

/// There is no content to send for this request, but the headers
/// may be useful. The user-agent may update its cached headers
/// for this resource with the new ones.
///
/// https://tools.ietf.org/html/rfc7231#section-6.3.5
noContent(204, 'No Content'),

/// This response means that server could not understand
/// the request due to invalid syntax.
///
/// https://tools.ietf.org/html/rfc7231#section-6.5.1
badRequest(400, 'Bad Request'),

/// Although the HTTP standard specifies "unauthorized", semantically
/// this response means "unauthenticated". That is, the client must
/// authenticate itself to get the requested response.
///
/// https://tools.ietf.org/html/rfc7235#section-3.1
unauthorized(401, 'Unauthorized'),

/// The client does not have access rights to the content, i.e.
/// they are unauthorized, so server is rejecting to give proper response.
/// Unlike 401, the client's identity is known to the server.
///
/// https://tools.ietf.org/html/rfc7231#section-6.5.3
forbidden(403, 'Forbidden'),

/// The server can not find requested resource.
///
/// In the browser, this means the URL is not recognized.
/// In an API, this can also mean that the endpoint is valid but
/// the resource itself does not exist.
///
/// Servers may also send this response instead of 403 to hide
/// the existence of a resource from an unauthorized client.
///
/// This response code is probably the most famous one due to
/// its frequent occurrence on the web.
///
/// https://tools.ietf.org/html/rfc7231#section-6.5.4
notFound(404, 'Not Found'),

/// This response would be sent when the requested content has been
/// permanently deleted from server, with no forwarding address.
///
/// Clients are expected to remove their caches and links to the resource.
///
/// The HTTP specification intends this status code to be used for
/// "limited-time, promotional services".
///
/// APIs should not feel compelled to indicate resources that have been
/// deleted with this status code.
///
/// https://tools.ietf.org/html/rfc7231#section-6.5.9
gone(410, 'Gone'),

/// The request was well-formed but was unable to be followed
/// due to semantic errors.
///
/// https://tools.ietf.org/html/rfc2518#section-10.3
unprocessableEntity(422, 'Unprocessable Entity'),

/// The user has sent too many requests
/// in a given amount of time ("rate limiting").
///
/// https://tools.ietf.org/html/rfc6585#section-4
tooManyRequests(429, 'Too Many Requests'),

/// The server encountered an unexpected condition that prevented it
/// from fulfilling the request.
///
/// https://tools.ietf.org/html/rfc7231#section-6.6.1
internalServerError(500, 'Internal Server Error'),

/// The request method is not supported by the server and cannot be handled.
///
/// The only methods that servers are required to support (and therefore
/// that must not return this code) are GET and HEAD.
///
/// https://tools.ietf.org/html/rfc7231#section-6.6.2
notImplemented(501, 'Not Implemented'),

/// This error response means that the server, while working as a gateway
/// to get a response needed to handle the request, got an invalid response.
///
/// https://tools.ietf.org/html/rfc7231#section-6.6.3
badGateway(502, 'Bad Gateway'),

/// The server is not ready to handle the request.
///
/// Common causes are a server that is down for maintenance or that is
/// overloaded. Note that together with this response, a user-friendly page
/// explaining the problem should be sent.
///
/// This responses should be used for temporary conditions
/// and the Retry-After: HTTP header should, if possible, contain
/// the estimated time before the recovery of the service.
///
/// The webmaster must also take care about the caching-related headers
/// that are sent along with this response, as these temporary condition
/// responses should usually not be cached.
///
/// https://tools.ietf.org/html/rfc7231#section-6.6.4
serviceUnavailable(503, 'Service Unavailable');

/// The http status code.
final int code;

/// The http status message.
final String message;

/// Returns the http status of [code].
static HttpStatus valueOf(final int code) {
for (final status in values) {
if (status.code == code) {
return status;
}
}

throw UnsupportedError('Unsupported value [$code].');
}

/// Returns true if this [code] equals to passed [code], otherwise false.
bool equalsByCode(final int code) => this.code == code;

const HttpStatus(this.code, this.message);
}
5 changes: 5 additions & 0 deletions lib/src/service/base_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import '../core/exception/data_not_found_exception.dart';
import '../core/exception/mastodon_exception.dart';
import '../core/exception/rate_limit_exceeded_exception.dart';
import '../core/exception/unauthorized_exception.dart';
import '../core/http_status.dart';
import '../core/service_helper.dart';
import '../core/util/json_utils.dart';
import 'entities/empty.dart';
Expand Down Expand Up @@ -211,6 +212,7 @@ abstract class BaseService implements _Service {
) =>
MastodonResponse(
headers: response.headers,
status: HttpStatus.valueOf(response.statusCode),
rateLimit: RateLimit.fromJson(
rateLimitConverter.convert(response.headers),
),
Expand All @@ -224,6 +226,7 @@ abstract class BaseService implements _Service {
}) =>
MastodonResponse(
headers: response.headers,
status: HttpStatus.valueOf(response.statusCode),
rateLimit: RateLimit.fromJson(
rateLimitConverter.convert(response.headers),
),
Expand All @@ -241,6 +244,7 @@ abstract class BaseService implements _Service {

return MastodonResponse(
headers: response.headers,
status: HttpStatus.valueOf(response.statusCode),
rateLimit: RateLimit.fromJson(
rateLimitConverter.convert(response.headers),
),
Expand All @@ -258,6 +262,7 @@ abstract class BaseService implements _Service {

return MastodonResponse(
headers: response.headers,
status: HttpStatus.valueOf(response.statusCode),
rateLimit: RateLimit.fromJson(
rateLimitConverter.convert(response.headers),
),
Expand Down
5 changes: 5 additions & 0 deletions lib/src/service/response/mastodon_response.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// modification, are permitted provided the conditions.

// 馃寧 Project imports:
import '../../core/http_status.dart';
import '../entities/empty.dart';
import '../entities/rate_limit.dart';

Expand All @@ -11,13 +12,17 @@ class MastodonResponse<D> {
/// Returns the new instance of [MastodonResponse].
const MastodonResponse({
required this.headers,
required this.status,
required this.rateLimit,
required this.data,
});

/// The headers of this response.
final Map<String, String> headers;

/// The HTTP status from Mastodon API server.
final HttpStatus status;

/// The rate limit
final RateLimit rateLimit;

Expand Down
85 changes: 85 additions & 0 deletions test/src/core/http_status_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2023 Kato Shinya. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided the conditions.

// 馃摝 Package imports:
import 'package:mastodon_api/src/core/http_status.dart';
import 'package:test/test.dart';

void main() {
test('.name', () {
expect(HttpStatus.ok.name, 'ok');
expect(HttpStatus.created.name, 'created');
expect(HttpStatus.accepted.name, 'accepted');
expect(HttpStatus.noContent.name, 'noContent');
expect(HttpStatus.badRequest.name, 'badRequest');
expect(HttpStatus.unauthorized.name, 'unauthorized');
expect(HttpStatus.forbidden.name, 'forbidden');
expect(HttpStatus.notFound.name, 'notFound');
expect(HttpStatus.gone.name, 'gone');
expect(HttpStatus.unprocessableEntity.name, 'unprocessableEntity');
expect(HttpStatus.tooManyRequests.name, 'tooManyRequests');
expect(HttpStatus.internalServerError.name, 'internalServerError');
expect(HttpStatus.notImplemented.name, 'notImplemented');
expect(HttpStatus.badGateway.name, 'badGateway');
expect(HttpStatus.serviceUnavailable.name, 'serviceUnavailable');
});

test('.code', () {
expect(HttpStatus.ok.code, 200);
expect(HttpStatus.created.code, 201);
expect(HttpStatus.accepted.code, 202);
expect(HttpStatus.noContent.code, 204);
expect(HttpStatus.badRequest.code, 400);
expect(HttpStatus.unauthorized.code, 401);
expect(HttpStatus.forbidden.code, 403);
expect(HttpStatus.notFound.code, 404);
expect(HttpStatus.gone.code, 410);
expect(HttpStatus.unprocessableEntity.code, 422);
expect(HttpStatus.tooManyRequests.code, 429);
expect(HttpStatus.internalServerError.code, 500);
expect(HttpStatus.notImplemented.code, 501);
expect(HttpStatus.badGateway.code, 502);
expect(HttpStatus.serviceUnavailable.code, 503);
});

test('.message', () {
expect(HttpStatus.ok.message, 'OK');
expect(HttpStatus.created.message, 'Created');
expect(HttpStatus.accepted.message, 'Accepted');
expect(HttpStatus.noContent.message, 'No Content');
expect(HttpStatus.badRequest.message, 'Bad Request');
expect(HttpStatus.unauthorized.message, 'Unauthorized');
expect(HttpStatus.forbidden.message, 'Forbidden');
expect(HttpStatus.notFound.message, 'Not Found');
expect(HttpStatus.gone.message, 'Gone');
expect(HttpStatus.unprocessableEntity.message, 'Unprocessable Entity');
expect(HttpStatus.tooManyRequests.message, 'Too Many Requests');
expect(HttpStatus.internalServerError.message, 'Internal Server Error');
expect(HttpStatus.notImplemented.message, 'Not Implemented');
expect(HttpStatus.badGateway.message, 'Bad Gateway');
expect(HttpStatus.serviceUnavailable.message, 'Service Unavailable');
});

group('.valueOf', () {
test('normal case', () {
final actual = HttpStatus.valueOf(200);

expect(actual, HttpStatus.ok);
});

test('when value is unsupported', () {
expect(() => HttpStatus.valueOf(999), throwsA(isA<UnsupportedError>()));
});
});

group('.equalsByCode', () {
test('when code equals to element', () {
expect(HttpStatus.ok.equalsByCode(200), isTrue);
});

test('when code does not equal to element', () {
expect(HttpStatus.ok.equalsByCode(201), isFalse);
});
});
}

0 comments on commit c2b8e49

Please sign in to comment.