From 5c87c018cf5a73cffae167867ab9582adbc88a34 Mon Sep 17 00:00:00 2001 From: Paul Schmiedmayer Date: Sat, 17 Jul 2021 17:08:52 +0200 Subject: [PATCH] Improve README and example startup --- App/App/SampleApp.swift | 7 +- App/AppUITests/AppUITests.swift | 1 + Example.xcworkspace/contents.xcworkspacedata | 4 +- README.md | 133 +++++++++++++++++- .../Sources/ExampleWebService/Example.swift | 13 +- 5 files changed, 146 insertions(+), 12 deletions(-) diff --git a/App/App/SampleApp.swift b/App/App/SampleApp.swift index 6e4826e..454585b 100644 --- a/App/App/SampleApp.swift +++ b/App/App/SampleApp.swift @@ -1,15 +1,16 @@ import SwiftUI import Model +import Foundation @main struct ExampleApp: App { @StateObject var model: Model = { - #if DEBUG + if ProcessInfo.processInfo.arguments.contains("AppUITests") || ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" { return LocalStorageModel() - #else + } else { return RestfulModel() - #endif + } }() diff --git a/App/AppUITests/AppUITests.swift b/App/AppUITests/AppUITests.swift index 843b8b3..bf41a1e 100644 --- a/App/AppUITests/AppUITests.swift +++ b/App/AppUITests/AppUITests.swift @@ -40,6 +40,7 @@ class AppUITests: XCTestCase { func testAddContact() throws { // UI tests must launch the application that they test. let app = XCUIApplication() + app.launchArguments = ["AppUITests"] app.launch() // Go to the new contacts screen app.navigationBars["Your Contacts"].buttons["Add"].tap() diff --git a/Example.xcworkspace/contents.xcworkspacedata b/Example.xcworkspace/contents.xcworkspacedata index 1439f96..c28760b 100644 --- a/Example.xcworkspace/contents.xcworkspacedata +++ b/Example.xcworkspace/contents.xcworkspacedata @@ -2,10 +2,10 @@ + location = "group:docker-compose.yml"> + location = "group:docker-compose-development.yml"> diff --git a/README.md b/README.md index ab1ab05..5e89a46 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,135 @@ [![Build and Test](https://github.com/Apodini/ApodiniExample/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/Apodini/ApodiniExample/actions/workflows/build-and-test.yml) [![Build Docker Compose](https://github.com/Apodini/ApodiniExample/actions/workflows/docker-compose.yml/badge.svg)](https://github.com/Apodini/ApodiniExample/actions/workflows/docker-compose.yml) -This repository includes an example Apodini web service, a shared Swift Package and an iOS App that can be used as a starting point for an Apodini web service. +This repository includes an example Apodini web service, a shared Swift Package, and an iOS App that can be used as a starting point for an Apodini web service. + +## Run the Example System + +You can start the Apodini example web services on any system that supports [docker](https://www.docker.com) and [docker compose](https://docs.docker.com/compose/). Follow the instructions on https://docs.docker.com/compose/install/ to install docker and docker compose. +To start and test the web service, you can run the `$ docker compose up` command to start the web service. + +Xcode 13 (only available on macOS) is required to build and run the example client application. Follow the instructions on https://developer.apple.com/xcode/ to install the latest version of Xcode. + +1. Opening the *Example.xcworkspace*. The workspace bundles the web services and the client application. +2. Select the *WebService* target, and then the *App* target and start the web service as well as the app by following the instructions on [Running Your App in the Simulator or on a Device](https://developer.apple.com/documentation/xcode/running-your-app-in-the-simulator-or-on-a-device) + +## System Functionality + +The example system features an example application to manage contacts and their residences. +Please note that this is a demo system and does not include any authentication or authorization mechanisms. +It uses a database to save the contacts and residences on the web service and includes examples of sharing code between a web service and the client application. + +### Web Service API + +You can test out the API by starting up the web service using the `$ docker compose up` command. + +**Create a New Contact Entry:** + +`POST` at `/v1/contacts`, e.g. [`http://localhost/v1/contacts`](http://localhost/v1/contacts) with a payload encoded in JSON (header `Content-Type` set to `application/json`) that encodes a contact: +```json +{ + "birthday": 648225181.40703702, + "name": "Paul" +} +``` +You can try out the following curl command to set a request to the gateway: +```bash +curl --header "Content-Type: application/json" \ + --request POST \ + --data '{"birthday": 648225181.40703702, "name": "Paul"}' \ + http://localhost/v1/contacts +``` + +**Get All Contacts or a Contact by ID** + +`GET` at `/v1/contacts`, e.g. [`http://localhost/v1/contacts`](http://localhost/v1/contacts) that returns the stored contacts. +You can try out the following curl command to get a list of contacts: +```bash +curl http://localhost/v1/contacts +``` + +You can get a single contact using `GET` at `/v1/contacts/{contactID}`, e.g. [`http://localhost/v1/contacts/E621E1F8-C36C-495A-93FC-0C247A3E6E5F`](http://localhost/v1/contacts/E621E1F8-C36C-495A-93FC-0C247A3E6E5F) that returns the stored contacts. +You can try out the following curl command to get a contact by ID: +```bash +curl http://localhost/v1/contacts/E621E1F8-C36C-495A-93FC-0C247A3E6E5F +``` + +**Update an Existing Contact:** + +`POST` at `/v1/contacts/{contactID}`, e.g. [`http://localhost/v1/contacts/E621E1F8-C36C-495A-93FC-0C247A3E6E5F`](http://localhost/v1/contacts/E621E1F8-C36C-495A-93FC-0C247A3E6E5F) with a payload encoded in JSON (header `Content-Type` set to `application/json`) that encodes a contact: +```json +{ + "birthday": 648225181.40703702, + "name": "Paul Schmiedmayer" +} +``` +You can try out the following curl command to set a request to the gateway: +```bash +curl --header "Content-Type: application/json" \ + --request PUT \ + --data '{"birthday": 648225181.40703702, "name": "Paul Schmiedmayer"}' \ + http://localhost/v1/contacts/E621E1F8-C36C-495A-93FC-0C247A3E6E5F +``` + +**Delete a Contact** + +You can delete a contact using `DELETE` at `/v1/contacts/{contactID}`, e.g. [`http://localhost/v1/contacts/E621E1F8-C36C-495A-93FC-0C247A3E6E5F`](http://localhost/v1/contacts/E621E1F8-C36C-495A-93FC-0C247A3E6E5F). +You can try out the following curl command to delete a contact: +```bash +curl --request DELETE \ + http://localhost/v1/contacts/E621E1F8-C36C-495A-93FC-0C247A3E6E5F +``` + +**Create a New Residence for a Contact:** + +`POST` at `/v1/residencies/`, e.g. [`http://localhost/v1/residencies`](http://localhost/v1/residencies) with a payload encoded in JSON (header `Content-Type` set to `application/json`) that encodes a contact: +```json +{ + "address": "Munich", + "contact": { + "id": "E621E1F8-C36C-495A-93FC-0C247A3E6E5F" + }, + "country": "Germany", + "postalCode": "80331" +} +``` +You can try out the following curl command to set a request to the gateway: +```bash +curl --header "Content-Type: application/json" \ + --request POST \ + --data '{"address": "Munich", "contact": {"id": "E621E1F8-C36C-495A-93FC-0C247A3E6E5F"}, "country": "Germany", "postalCode": "80331"}' \ + http://localhost/v1/residencies +``` + +**Get All Residence or a Residence by ID for a Contact** + +`GET` at `/v1/residencies`, e.g. [`http://localhost/v1/residencies`](http://localhost/v1/residencies) that returns the stored residencies. +You can try out the following curl command to get a list of contacts: +```bash +curl http://localhost/v1/residencies +``` + +You can get a single residencie using `GET` at `/v1/residencies/{residencieID}`, e.g. [`http://localhost/v1/residencies/E621E1F8-C36C-495A-93FC-0C247A3E6E5F`](http://localhost/v1/residencies/E621E1F8-C36C-495A-93FC-0C247A3E6E5F) that returns the stored residence. +You can try out the following curl command to get a residence by ID: +```bash +curl http://localhost/v1/residencies/E621E1F8-C36C-495A-93FC-0C247A3E6E5F +``` + +**Delete a Residence by ID for a Contact** + +You can delete a residence using `DELETE` at `/v1/residencies/{contactID}`, e.g. [`http://localhost/v1/residencies/E621E1F8-C36C-495A-93FC-0C247A3E6E5F`](http://localhost/v1/residencies/E621E1F8-C36C-495A-93FC-0C247A3E6E5F). +You can try out the following curl command to delete a residence: +```bash +curl --request DELETE \ + http://localhost/v1/residencies/E621E1F8-C36C-495A-93FC-0C247A3E6E5F +``` + +### Client Application + +You can use the functionality of the web service using the bundled client application. You can also test the functionality of the app using the bundled unit and UI tests. + +## Contributing +Contributions to this project are welcome. Please make sure to read the [contribution guidelines](https://github.com/Apodini/.github/blob/main/CONTRIBUTING.md) and the [contributor covenant code of conduct](https://github.com/Apodini/.github/blob/main/CODE_OF_CONDUCT.md) first. + +## License +This project is licensed under the MIT License. See [License](https://github.com/Apodini/ApodiniExample/blob/develop/LICENSE) for more information. diff --git a/WebService/Sources/ExampleWebService/Example.swift b/WebService/Sources/ExampleWebService/Example.swift index a413f3a..cbc9bc0 100644 --- a/WebService/Sources/ExampleWebService/Example.swift +++ b/WebService/Sources/ExampleWebService/Example.swift @@ -24,16 +24,16 @@ public struct Example: WebService { // Defines on which hostname and port the webservice should be bound to, configurable via CLI-arguments, else defaults HTTPConfiguration(port: port) - // Setup of example database (in this case SQlite) and add migrations to create the respective tables - DatabaseConfiguration(.sqlite(.file(databasePath))) - .addMigrations(ContactMigration()) - .addMigrations(ResidenceMigration()) - // If the appropriate CLI flag is passed, revert the database migrations if revertDatabaseMigrations { DatabaseRevertConfiguration(.sqlite(.file(databasePath))) .addMigrations(ContactMigration()) .addMigrations(ResidenceMigration()) + } else { + // Setup of example database (in this case SQlite) and add migrations to create the respective tables + DatabaseConfiguration(.sqlite(.file(databasePath))) + .addMigrations(ContactMigration()) + .addMigrations(ResidenceMigration()) } } @@ -41,6 +41,7 @@ public struct Example: WebService { ContactComponent() ResidenceComponent() } - + + public init() {} }