Skip to content

Commit ea1b9d7

Browse files
authored
Update README and add Getting Started Tutorial, Swift Package Plugin docs, CONTRIBUTING guidance + more (CreateAPI#82)
* Write 'Generating an API with CreateAPI' tutorial * Add a technical note about dependencies * Add a technical note about OpenAPI support * Add AdvancedSetup documentation * Add documentation for Swift Package Plugins * Add CONTRIBUTING.md * Update README.md * Move Releasing notes to CONTRINUTING.md * Add disclaimer about not editing generated package * Update Dependencies.md to reference Get as 1.0.2 or later * Update documentation to demonstrate using --split * Update Help text in README.md * Add better README introduction based on 0.1 behaviour * Update CONTRIBUTING.md to reflect changes to how we rcord snapshots * Update SwiftPackagePlugins to use 0.1 style arguments * Update Dependencies.md to use renamed option names * Rewrite the tutorial to focus entirely on Package generation * Update the version number to 0.1.0
1 parent 8c36d9c commit ea1b9d7

15 files changed

+614
-44
lines changed

CONTRIBUTING.md

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Contributing
2+
3+
Your contributions are always appreciated! To make it easier for both you and the maintainers to resolve your issues or ship your changes, please refer to the guidance below.
4+
5+
- [Bug Reports](#bug-reports)
6+
- [Feature Requests](#feature-requests)
7+
- [Pull Requests](#pull-requests)
8+
- [Releasing](#releasing)
9+
10+
## Bug Reports
11+
12+
If you have ran into an issue using CreateAPI, when creating an issue on GitHub, please make sure that you include the following information:
13+
14+
- The version of CreateAPI that you were using
15+
- A reproducible example including the schema, the config and the options that you passed to the generate command
16+
17+
When the instructions are clear and its easy for a maintainer to reproduce the issue, it makes it easier for everybody to find a solution.
18+
19+
While its not required, if you can reproduce the issue in the existing tests and open a draft Pull Request that demonstrates the issue, that is even better! You don't have to know how to fix it, but its a great start.
20+
21+
## Feature Requests
22+
23+
We want CreateAPI to be full of rich and useful features. If you have an idea, please open an issue to start a discussion!
24+
25+
## Pull Requests
26+
27+
Submitting Pull Requests is always welcome, this could be to fix bugs, refactor code, implement new features or even to add or update documentation.
28+
29+
If you are changing code, please be sure to make sure that the tests pass locally before submitting your Pull Request. You can do this by running `swift test` in terminal (or <kbd>⌘</kbd> + <kbd>U</kbd> in Xcode).
30+
31+
### Test Fixtures
32+
33+
The CreateAPITests target has a series of expected fixtures committed to the repository and when running tests, to verify that the generator works as expected, the output generated by the test must match the contents committed to the repo.
34+
35+
If you are making a change, ether to fix/reproduce a bug, or to add a new feature, you'll likely want to update the recorded snapshots to reflect your changes. To do this, in Xcode change the Scheme to "Record Snapshots" and run the entire suite of tests.
36+
37+
If you want to add a new spec or configuration to the tests, you can add the schema to **Tests/Support/Specs/** and then write a new test, with the desired configuration in **GenerateTests.swift**. Be sure to use the record mode on your first run.
38+
39+
### Writeup
40+
41+
When submitting a Pull Request, please be sure to include plenty of details for the reviewers. The amount of information you should include varies by the size of the change, but please explain the motivation for your changes and provide a summary of how you have achieved that.
42+
43+
If you can predict that there might be questions/confusion/concern about anything in your change, explaining that in the description, or providing an inline review of your own will greatly help to speed up the feedback loop and get your change shipped faster.
44+
45+
## Releasing
46+
47+
When releasing a new version of CreateAPI, be sure to follow the steps outlined below:
48+
49+
- [ ] Ensure that the `main` branch checks are passing.
50+
- [ ] Update the [version number](https://github.com/CreateAPI/CreateAPI/blob/main/Sources/CreateAPI/CreateAPI.swift#L8).
51+
- [ ] Update the `create-api generate --help` output in **README.md** if it has changed.
52+
- [ ] Update [CHANGELOG.md](./CHANGELOG.md).
53+
- Use the GitHub generated release notes as a base. Be sure to cleanup the PR links.
54+
- [ ] Create a GitHub Release.
55+
- Create a new tag using semantic versioning.
56+
- Use the generated release notes.
57+
- [ ] From the [Release Workflow](https://github.com/CreateAPI/CreateAPI/actions/workflows/release.yml), get the artifactbundle SHA and add an Artifact Bundle section to the release page. For example:
58+
> ## Artifact Bundle
59+
>
60+
> Checksum: `89c75ec3b2938d08b961b94e70e6dd6fa0ff52a90037304d41718cd5fb58bd24`
61+
>
62+
> ```swift
63+
> .binaryTarget(
64+
> name: "create-api",
65+
> url: "https://github.com/CreateAPI/CreateAPI/releases/download/0.0.5/create-api.artifactbundle.zip",
66+
> checksum: "89c75ec3b2938d08b961b94e70e6dd6fa0ff52a90037304d41718cd5fb58bd24"
67+
> )
68+
> ```
69+
- [ ] Push the release to Homebrew with `brew bump-formula-pr create-api`.
70+
71+
72+
---
73+
74+
Thanks again for contributing to CreateAPI!

Docs/AdvancedSetup.md

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Advanced Setup
2+
3+
Looking for examples that push CreateAPI to it's limits? Well look no further.
4+
5+
<!-- Be sure to update this TOC when adding new sections! -->
6+
- [Using a different API Client](#using-a-different-api-client)
7+
8+
## Using a different API Client
9+
10+
While using [Get](https://github.com/kean/get) is the easiest way to get started with CreateAPI, you might want to integrate CreateAPI with a different client instead.
11+
12+
To do this, there are two important steps that need following:
13+
14+
1. Exclude the `Get` import in the generated paths
15+
2. Write a matching `Request` type
16+
17+
### Exclude the `Get` import
18+
19+
Using the [`paths.imports`](./ConfigOptions.md#pathsimports) option, override the default value (`["Get"]`) to omit the import:
20+
21+
**.create-api.yaml**
22+
```yaml
23+
paths:
24+
# Ensure that Get is not imported in generated source files
25+
imports: []
26+
```
27+
28+
### Write a matching `Request` type
29+
30+
The generated code needs to initialize a type called `Request` to define all of the path parameters and response type. When importing Get, `Request` was already available to the generated code but after removing the import, this will no longer be the case.
31+
32+
You should either add a new `Request` type to the module of the generated code, or import one from a different module (be sure to update `paths.imports` if so). The type should have the same interface as [Get's `Request` type](https://github.com/kean/Get/blob/899db7397eacddad384fc252d79b804c0801072c/Sources/Get/Request.swift#L11-L118):
33+
34+
```swift
35+
struct Request<Response> {
36+
var method: String = "GET"
37+
var url: String
38+
var query: [(String, String?)]?
39+
var body: Encodable?
40+
var headers: [String: String]?
41+
var id: String?
42+
}
43+
```
44+
45+
The generated Paths will provide configured instances of this `Request` type that you can then pass into your own API client instead.

Docs/Dependencies.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Dependencies
2+
3+
By default, CreateAPI will use [Get](https://github.com/kean/Get) for the API client. If you are generating a package, this dependency is setup for you automatically but if you are generating for an existing module, you will need to manage dependencies yourself.
4+
5+
Below is a table that describes dependencies that CreateAPI generated code might need to use:
6+
7+
Dependency|Minimum Version|Required When?
8+
---|---|---
9+
[Get](https://github.com/kean/Get)|1.0.2|Generating Paths*
10+
[URLQueryEncoder](https://github.com/CreateAPI/URLQueryEncoder)|0.0.2|Generating paths with query parameters
11+
[HTTPHeaders](https://github.com/CreateAPI/HTTPHeaders)|0.1.0|[`includeResponseHeaders`](./ConfigOptions.md#pathsincluderesponseheaders) is set to `true` (the default)
12+
[NaiveDate](https://github.com/CreateAPI/NaiveDate)|1.0.0|[`useNaiveDate`](./ConfigOptions.md#usenaivedate) is set to `true` (the default)
13+
14+
15+
> **Note**: _*If you are already using a different API client and don't want to have to depend on Get, check out the [Advanced Setup](./AdvancedSetup.md#using-a-different-api-client) documentation._
1.09 MB
Loading
1.15 MB
Loading
903 KB
Loading
Loading
811 KB
Loading
861 KB
Loading

Docs/OpenAPISupport.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# OpenAPI Support
2+
3+
The goal is to completely cover OpenAPI 3.x spec.
4+
5+
Currently, the following features are **not** supported:
6+
7+
- External References
8+
9+
Some discrepancies with the OpenAPI spec are by design:
10+
11+
- `allowReserved` keyword in parameters is ignored and all parameter values are percent-encoded
12+
- `allowEmptyValue` keyword in parameters is ignored as it's not recommended to be used

Docs/README.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Documentation
2+
3+
Here you can find documentation specific for using CreateAPI.
4+
5+
- [Tutorial: Generating an API with CreateAPI](./Tutorial.md)
6+
- [Configuration Options](./ConfigOptions.md)
7+
- [Swift Package Plugins](./SwiftPackagePlugins.md)
8+
- [Dependencies](./Dependencies.md)
9+
- [OpenAPI Support](./OpenAPISupport.md)
10+
- [Advanced Setup](./AdvancedSetup.md)

Docs/SwiftPackagePlugins.md

+214
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Swift Package Plugins
2+
3+
Plugins were introduced for Swift Package Manager in Swift 5.6 (Xcode 13.4) and they have great potential to streamline your developer experience.
4+
5+
If you are new to Plugins, be sure to check out some more general introductions from the resources below:
6+
7+
- [Meet Swift Package plugins - Apple WWDC 2022](https://developer.apple.com/videos/play/wwdc2022/110359/)
8+
- [Create Swift Package plugins - Apple WWDC 2022](https://developer.apple.com/videos/play/wwdc2022/110401/)
9+
10+
If you want to learn how to use Swift Package plugins with CreateAPI, we've explored use cases with both Command and Build Tool plugins.
11+
12+
> **Warning**: Plugins are a new concept, and we're still learning about them ourself. Expect this documentation to change frequently so be sure to check back for new best practices.
13+
14+
## Depending on `create-api`
15+
16+
While there are different types of plugins, you are always going to need the `create-api` cli as a dependency.
17+
18+
You can do this by adding the CreateAPI package as a dependency just like you would with any other library but this means that you need to compile it each time, which isn't ideal.
19+
20+
Instead, you can add your own target that links to our published Artifact Bundle to bring in a precompiled version of the tool.
21+
22+
> **Note**: Currently, our Artifact Bundle only includes the precompiled binary for macOS meaning that there is no Linux support.
23+
24+
To add a new `binaryTarget` to your package, you need two pieces of information:
25+
26+
1. The url to the artifact bundle
27+
2. The checksum of the bundle
28+
29+
You can find all of this in the [release notes](https://github.com/CreateAPI/CreateAPI/releases/latest).
30+
31+
In your **Package.swift**, add the following to your `targets` array:
32+
33+
```swift
34+
.binaryTarget(
35+
name: "create-api",
36+
url: "https://github.com/CreateAPI/CreateAPI/releases/download/x.x.x/create-api.artifactbundle.zip",
37+
checksum: "ffffffffff"
38+
)
39+
```
40+
41+
Replace the `url` and `checksum` with the values for the release of your choice.
42+
43+
## Writing a Plugin
44+
45+
- [Command Plugin](#command-plugin)
46+
- [Build Tool Plugin](#build-tool-plugin)
47+
48+
### Command Plugin
49+
50+
A command plugin can be invoked on demand via the `swift package` command.
51+
52+
Depending on your usage, you might then write a plugin that just invokes the `create-api` cli with the appropriate options and arguments, or you might do something more complex like invoke the command multiple times for multiple different modules. It's entirely up to you.
53+
54+
Below is a basic example of a `CommandPlugin`:
55+
56+
**Plugins/GenerateAPI/Plugin.swift**
57+
```swift
58+
import Foundation
59+
import PackagePlugin
60+
61+
@main
62+
struct Plugin: CommandPlugin {
63+
func performCommand(context: PluginContext, arguments: [String]) async throws {
64+
let createAPI = try context.tool(named: "create-api")
65+
let workingDirectory = context.package.directory.appending("Sources", "MyAPI")
66+
67+
let process = Process()
68+
process.currentDirectoryURL = URL(fileURLWithPath: workingDirectory.string)
69+
process.executableURL = URL(fileURLWithPath: createAPI.path.string)
70+
process.arguments = [
71+
"generate",
72+
"schema.json",
73+
"--config", ".create-api.yml",
74+
"--output", "Generated"
75+
]
76+
77+
try process.run()
78+
process.waitUntilExit()
79+
}
80+
}
81+
```
82+
83+
**Package.swift**
84+
```swift
85+
// swift-tools-version:5.6
86+
import PackageDescription
87+
88+
let package = Package(
89+
name: "MyPackage",
90+
platforms: [.iOS(.v13), .macOS(.v10_15)],
91+
products: [.library(name: "MyAPI", targets: ["MyAPI"])],
92+
dependencies: [
93+
// ...
94+
],
95+
targets: [
96+
.target(
97+
name: "MyAPI",
98+
exclude: ["schema.json", ".create-api.yml"]
99+
),
100+
.binaryTarget(
101+
name: "create-api",
102+
url: "https://github.com/CreateAPI/CreateAPI/releases/download/0.0.5/create-api.artifactbundle.zip",
103+
checksum: "89c75ec3b2938d08b961b94e70e6dd6fa0ff52a90037304d41718cd5fb58bd24"
104+
),
105+
.plugin(
106+
name: "CreateAPI",
107+
capability: .command(
108+
intent: .custom(
109+
verb: "generate-api",
110+
description: "Generates the OpenAPI entities and paths using CreateAPI"
111+
),
112+
permissions: [
113+
.writeToPackageDirectory(reason: "To output the generated source code")
114+
]
115+
),
116+
dependencies: [
117+
.target(name: "create-api")
118+
]
119+
)
120+
]
121+
)
122+
```
123+
124+
You could then invoke the plugin like so:
125+
126+
```bash
127+
$ swift package --allow-writing-to-package-directory generate-api
128+
```
129+
130+
In the above example, the plugin will call `create-api generate` within the **Sources/MyAPI/** directory where it loads the **schema.json** and **.create-api.yml** files and outputs the sources inline. You can then review the output and commit them etc.
131+
132+
### Build Tool Plugin
133+
134+
A build tool plugin works slightly differently. Instead of manually running it through the `swift package` command, its run as part of the build process meaning that you can do things such as generate the source code only when required and avoid having to check it into source control.
135+
136+
Below is a basic example of a `BuildToolPlugin`:
137+
138+
**Plugins/GenerateAPI/Plugin.swift**
139+
```swift
140+
import Foundation
141+
import PackagePlugin
142+
143+
@main
144+
struct Plugin: BuildToolPlugin {
145+
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
146+
let schema = context.package.directory.appending("schema.yml")
147+
let config = context.package.directory.appending(".create-api.yml")
148+
let output = context.pluginWorkDirectory
149+
150+
return [
151+
.buildCommand(
152+
displayName: "Generating API",
153+
executable: try context.tool(named: "create-api").path,
154+
arguments: [
155+
"generate",
156+
schema,
157+
"--output", output,
158+
"--config", config,
159+
"--config-option", "module=\(target.name)",
160+
"--config-option", "mergeSources=true"
161+
],
162+
inputFiles: [
163+
schema,
164+
config
165+
],
166+
outputFiles: [
167+
output.appending("Paths.swift"),
168+
output.appending("Entities.swift")
169+
]
170+
)
171+
]
172+
}
173+
}
174+
```
175+
176+
**Package.swift**
177+
```swift
178+
// swift-tools-version: 5.6
179+
import PackageDescription
180+
181+
let package = Package(
182+
name: "MyPackage",
183+
platforms: [.iOS(.v13), .macOS(.v10_15)],
184+
products: [.library(name: "MyAPI", targets: ["MyAPI"])],
185+
dependencies: [
186+
// ...
187+
],
188+
targets: [
189+
.target(
190+
name: "MyAPI",
191+
dependencies: [
192+
.target(name: "GenerateAPI"),
193+
// ...
194+
]
195+
),
196+
.binaryTarget(
197+
name: "create-api",
198+
url: "https://github.com/CreateAPI/CreateAPI/releases/download/0.0.5/create-api.artifactbundle.zip",
199+
checksum: "89c75ec3b2938d08b961b94e70e6dd6fa0ff52a90037304d41718cd5fb58bd24"
200+
),
201+
.plugin(
202+
name: "GenerateAPI",
203+
capability: .buildTool(),
204+
dependencies: [
205+
.target(name: "create-api")
206+
]
207+
)
208+
]
209+
)
210+
```
211+
212+
While there are some similarities to command plugins, the build tool plugin is marked as a dependency to the relevant target (`MyAPI`) and the plugin returns a build command provides to the build system so that it can determine if it needs to run or not. It does this by checking both the defined `inputs` and `outputs`. It the outputs are missing, or the inputs have changed, it'll run the `create-api generate` command and write the outputs into `pluginWorkDirectory`.
213+
214+
This is powerful, because it enables you to modify the schema or configuration file inside Xcode, hit <kbd>⌘</kbd> + <kbd>B</kbd> and see your changes reflected instantaneously. It does however have some downsides because it is not easy to find the generated sources and can make debugging schema/generation issues tricker.

0 commit comments

Comments
 (0)