Skip to content
This repository has been archived by the owner on Sep 30, 2024. It is now read-only.

Commit

Permalink
Merge branch 'release/0.5.0' into versions
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeehut committed Sep 28, 2021
2 parents 9db1613 + fd04b31 commit 8609a78
Show file tree
Hide file tree
Showing 19 changed files with 1,305 additions and 81 deletions.
8 changes: 4 additions & 4 deletions .sourcery/LinuxMain.stencil
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import XCTest

{% for type in types.classes|based:"XCTestCase" %}
extension {{ type.name }} {
static var allTests: [(String, ({{ type.name }}) -> () throws -> Void)] = [
{% for method in type.methods where method.parameters.count == 0 and method.shortName|hasPrefix:"test" and method|!annotated:"skipTestOnLinux" %} ("{{ method.shortName }}", {{ method.shortName }}){% if not forloop.last %},{% endif %}
{% endfor %}]
static var allTests: [(String, ({{ type.name }}) -> () throws -> Void)] = [
{% for method in type.methods where method.parameters.count == 0 and method.shortName|hasPrefix:"test" and method|!annotated:"skipTestOnLinux" %} ("{{ method.shortName }}", {{ method.shortName }}){% if not forloop.last %},{% endif %}
{% endfor %}]
}

{% endfor %}
XCTMain([
{% for type in types.classes|based:"XCTestCase" %} testCase({{ type.name }}.allTests){% if not forloop.last %},{% endif %}
{% for type in types.classes|based:"XCTestCase" %} testCase({{ type.name }}.allTests){% if not forloop.last %},{% endif %}
{% endfor %}])
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
### Security
- None.

## [0.5.0] - 2021-09-28
### Added
- New `mockingBehavior` parameter on `ApiProvider` for testing purposes. Specify one with `delay` and `scheduler`, e.g. `.seconds(0.5)` and `DispatchQueue.main.eraseToAnyScheduler()`. Provides `nil` by default to make actual requests.
- New optional `mockedResponse` computed property on `Endpoint` protocol expecting an output of type `MockedResponse`. Use this to provide mocked responses when using a `mockingBehavior` in tests. See the [PostmanEchoEndpoint](https://github.com/Flinesoft/Microya/blob/main/Tests/MicroyaTests/Supporting/PostmanEchoEndpoint.swift#L114-127) in the tests for a usage example via the `mock` convenience method.
### Changed
- Moved `baseUrl` from `Endpoint` to `ApiProvider`. This allows for specifying different `baseUrl` even when `Endpoint` is implemented in a library by passing it in the app.
- Renamed `HttpBasicAuthPlugin` to `HttpAuthPlugin` with a new `scheme` parameter that accepts one of `.basic` or `.bearer` to support multiple authentication methods.

## [0.4.0] - 2020-11-21
### Added
- Microya now supports Combine publishers, just call `publisher(on:decodeBodyTo:)` or `publisher(on:)` instead of `performRequest(on:decodeBodyTo:)` or `performRequest(on:)` and you'll get an `AnyPublisher` request stream to subscribe to. In success cases you will receive the decoded typed object, in error cases an `ApiError` object exactly like within the `performRequest` completion closure. But instead of a `Result` type you can use `sink` or `catch` from the Combine framework.
Expand Down
25 changes: 25 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"object": {
"pins": [
{
"package": "combine-schedulers",
"repositoryURL": "https://github.com/pointfreeco/combine-schedulers.git",
"state": {
"branch": null,
"revision": "c37e5ae8012fb654af776cc556ff8ae64398c841",
"version": "0.5.0"
}
},
{
"package": "xctest-dynamic-overlay",
"repositoryURL": "https://github.com/pointfreeco/xctest-dynamic-overlay",
"state": {
"branch": null,
"revision": "603974e3909ad4b48ba04aad7e0ceee4f077a518",
"version": "0.1.0"
}
}
]
},
"version": 1
}
13 changes: 11 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.3
// swift-tools-version:5.4
import PackageDescription

let package = Package(
Expand All @@ -7,8 +7,17 @@ let package = Package(
products: [
.library(name: "Microya", targets: ["Microya"])
],
dependencies: [
// ⏰ A few schedulers that make working with Combine more testable and more versatile.
.package(url: "https://github.com/pointfreeco/combine-schedulers.git", from: "0.5.0")
],
targets: [
.target(name: "Microya"),
.target(
name: "Microya",
dependencies: [
.product(name: "CombineSchedulers", package: "combine-schedulers")
]
),
.testTarget(
name: "MicroyaTests",
dependencies: ["Microya"]
Expand Down
57 changes: 51 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
alt="codebeat badge">
</a>
<a href="https://github.com/Flinesoft/HandySwift/releases">
<img src="https://img.shields.io/badge/Version-0.4.0-blue.svg"
alt="Version: 0.4.0">
<img src="https://img.shields.io/badge/Version-0.5.0-blue.svg"
alt="Version: 0.5.0">
<img src="https://img.shields.io/badge/Swift-5.3-FFAC45.svg"
alt="Swift: 5.3">
<img src="https://img.shields.io/badge/Platforms-Apple%20%7C%20Linux-FF69B4.svg"
Expand Down Expand Up @@ -55,6 +55,8 @@ A micro version of the [Moya](https://github.com/Moya/Moya) network abstraction

Installation is only supported via [SwiftPM](https://github.com/apple/swift-package-manager).

> :warning: If you need to support platform where the `Combine` framework is not available (< iOS/tvOS 13, < macOS 10.15), please use the `support/without-combine` branch instead.
## Usage

### Step 1: Defining your Endpoints
Expand Down Expand Up @@ -83,6 +85,7 @@ public protocol Endpoint {
var subpath: String { get }
var method: HttpMethod { get }
var queryParameters: [String: QueryParameterValue] { get }
var mockedResponse: MockedResponse? { get }
}
```

Expand Down Expand Up @@ -135,7 +138,7 @@ extension MicrosoftTranslatorEndpoint: Endpoint {
case .languages:
return .get

case let .translate(texts, _, _):
case let .translate(texts, _, _, _):
return .post(try! encoder.encode(texts))
}
}
Expand All @@ -147,13 +150,24 @@ extension MicrosoftTranslatorEndpoint: Endpoint {
case .languages:
break

case let .translate(_, sourceLanguage, targetLanguages, _):
case let .translate(_, sourceLanguage, targetLanguages):
queryParameters["from"] = .string(sourceLanguage.rawValue)
queryParameters["to"] = .array(targetLanguages.map { $0.rawValue })
}

return queryParameters
}

var mockedResponse: MockedResponse? {
switch self {
case .languages:
return mock(status: .ok, bodyJson: #"{ "languages: ["de", "en", "fr", "ja"] }"#)

case let .translate(texts, _, _):
let pseudoTranslationsJson = texts.map { $0.reversed() }.joined(separator: ",")
return mock(status: .ok, bodyJson: "[\(pseudoTranslationsJson)]")
}
}
}
```

Expand Down Expand Up @@ -235,8 +249,6 @@ There's even useful functional methods defined on the `Result` type like `map()`

### Combine Support

`performRequest(on:decodeBodyTo:)` or `performRequest()`

If you are using Combine in your project (e.g. because you're using SwiftUI), you might want to replace the calls to `performRequest(on:decodeBodyTo:)` or `performRequest(on:)` with the Combine calls `publisher(on:decodeBodyTo:)` or `publisher(on:)`. This will give you an `AnyPublisher` request stream to subscribe to. In success cases you will receive the decoded typed object, in error cases an `ApiError` object exactly like within the `performRequest` completion closure. But instead of a `Result` type you can use `sink` or `catch` from the Combine framework.

For example, the usage with Combine might look something like this:
Expand Down Expand Up @@ -330,6 +342,8 @@ public var headers: [String: String] {
}

public var queryParameters: [String: QueryParameterValue] { [:] }

public var mockedResponse: MockedResponse? { nil }
```

So technically, the `Endpoint` type only requires you to specify the following 4 things:
Expand All @@ -346,6 +360,37 @@ protocol Endpoint {
This can be a time (/ code) saver for simple APIs you want to access.
You can also use `EmptyBodyResponse` type for `ClientErrorType` to ignore the client error body structure.

### Testing

Microya supports mocking responses in your tests.
To do that, just initialize a different `ApiProvider` in your tests and specify with a given `delay` and `scheduler` as the `mockingBehavior` parameter.

Now, instead of making actual calls, Microya will respond with the provided `mockedResponse` computed property in your `Endpoint` type.

Note that the `.delay` mocking behavior is designed for use with Combine schedulers. Use `DispatchQueue.test` from the [`combine-schedulers` library](https://github.com/pointfreeco/combine-schedulers) (which is included with Microya) to control time in your tests so you don't need to actually wait for the requests when using `.delay`.

For example, you might want to add an extension in your tests to provide a `.mocked` property to use whenever you need an `ApiProvider` like so:

```swift
import CombineSchedulers
import Foundation
import Microya

let testScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.test

extension ApiProvider {
static var mocked: ApiProvider<MicrosoftTranslatorEndpoint> {
ApiProvider<MicrosoftTranslatorEndpoint>(
baseUrl: URL(string: "https://api.cognitive.microsofttranslator.com")!,
mockingBehavior: MockingBehavior(delay: .seconds(0.5), scheduler: testScheduler.eraseToAnyScheduler()
)
}
}

```

Now, in your tests you can just call `testScheduler.advance(by: .milliseconds(300))` fast-forward the time so your tests stay fast.

## Donation

Microya was brought to you by [Cihat Gündüz](https://github.com/Jeehut) in his free time. If you want to thank me and support the development of this project, please **make a small donation on [PayPal](https://paypal.me/Dschee/5EUR)**. In case you also like my other [open source contributions](https://github.com/Flinesoft) and [articles](https://medium.com/@Jeehut), please consider motivating me by **becoming a sponsor on [GitHub](https://github.com/sponsors/Jeehut)** or a **patron on [Patreon](https://www.patreon.com/Jeehut)**.
Expand Down
28 changes: 28 additions & 0 deletions Sources/Microya/Core/ApiError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,32 @@ public enum ApiError<ClientErrorType: Decodable>: Error {

/// Server responded with a non HTTP response, although an HTTP request was made. Either a bug in `JsonApi` or on the server side.
case unexpectedResponseType(response: URLResponse)

/// The `mockingBehavior` was set to non-nil (for testing) but no `mockedResponse` was provided for the requested endpoint.
case emptyMockedResponse
}

extension ApiError: Equatable where ClientErrorType: Equatable {
public static func == (lhs: ApiError<ClientErrorType>, rhs: ApiError<ClientErrorType>) -> Bool {
switch (lhs, rhs) {
case (.noResponseReceived, .noResponseReceived),
(.unexpectedResponseType, .unexpectedResponseType),
(.emptyMockedResponse, .emptyMockedResponse):
return true

case let (.noDataInResponse(leftStatusCode), .noDataInResponse(rightStatusCode)),
let (.unexpectedStatusCode(leftStatusCode), .unexpectedStatusCode(rightStatusCode)),
let (.serverError(leftStatusCode), .serverError(rightStatusCode)):
return leftStatusCode == rightStatusCode

case let (.clientError(leftStatusCode, leftClientError), .clientError(rightStatusCode, rightClientError)):
return leftStatusCode == rightStatusCode && leftClientError == rightClientError

case let (.responseDataConversionFailed(leftType, _), .responseDataConversionFailed(rightType, _)):
return leftType == rightType

default:
return false
}
}
}
Loading

0 comments on commit 8609a78

Please sign in to comment.