Skip to content

Commit 45a5bfd

Browse files
authored
improvements (#1)
* Update README.md * improve logging * refactor app name * update docs * update readme * add CI * fix build * Update README.md * add more docs
1 parent 85f37ef commit 45a5bfd

File tree

17 files changed

+305
-113
lines changed

17 files changed

+305
-113
lines changed

.github/workflows/ci.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
name: ci
2+
on:
3+
push:
4+
branches: [ main ]
5+
pull_request:
6+
branches: '*'
7+
env:
8+
CI_XCODE_13: '/Applications/Xcode_13.4.app/Contents/Developer'
9+
10+
jobs:
11+
spm-test:
12+
runs-on: macos-12
13+
steps:
14+
- uses: actions/checkout@v3
15+
- name: Create and set the default keychain
16+
run: |
17+
security create-keychain -p "" temporary
18+
security default-keychain -s temporary
19+
security unlock-keychain -p "" temporary
20+
security set-keychain-settings -lut 7200 temporary
21+
- name: Use multiple cores
22+
run: defaults write com.apple.dt.XCBuild EnableSwiftBuildSystemIntegration 1
23+
- name: Build and Test
24+
run: swift test --enable-code-coverage -v
25+
env:
26+
DEVELOPER_DIR: ${{ env.CI_XCODE_13 }}
27+
- name: Prepare codecov
28+
uses: sersoft-gmbh/swift-coverage-action@v2
29+
id: coverage-files
30+
with:
31+
format: lcov
32+
search-paths: ./.build
33+
env:
34+
DEVELOPER_DIR: ${{ env.CI_XCODE_13 }}
35+
- name: Upload coverage to Codecov
36+
uses: codecov/codecov-action@v2
37+
with:
38+
files: ${{join(fromJSON(steps.coverage-files.outputs.files), ',')}}
39+
env_vars: SPM
40+
fail_ci_if_error: true
41+
env:
42+
DEVELOPER_DIR: ${{ env.CI_XCODE_13 }}
43+
44+
linux:
45+
runs-on: ubuntu-18.04
46+
steps:
47+
- uses: actions/checkout@v3
48+
- uses: sersoft-gmbh/SwiftyActions@v1
49+
with:
50+
release-version: "5"
51+
github-token: ${{ secrets.GITHUB_TOKEN }}
52+
- name: Build and Test
53+
run: swift test --enable-test-discovery --enable-code-coverage -v
54+
- name: Prepare codecov
55+
run: |
56+
llvm-cov export -format="lcov" .build/x86_64-unknown-linux-gnu/debug/ParseServerSwiftPackageTests.xctest -instr-profile .build/x86_64-unknown-linux-gnu/debug/codecov/default.profdata > info_linux.lcov
57+
- name: Upload coverage to Codecov
58+
uses: codecov/codecov-action@v2
59+
with:
60+
env_vars: LINUX
61+
fail_ci_if_error: true

Package.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ let package = Package(
66
platforms: [
77
.macOS(.v12)
88
],
9+
products: [
10+
.library(name: "ParseServerSwift", targets: ["ParseServerSwift"])
11+
],
912
dependencies: [
1013
// 💧 A server-side Swift web framework.
1114
.package(url: "https://github.com/vapor/vapor.git", .upToNextMajor(from: "4.62.0")),
@@ -15,7 +18,7 @@ let package = Package(
1518
],
1619
targets: [
1720
.target(
18-
name: "App",
21+
name: "ParseServerSwift",
1922
dependencies: [
2023
.product(name: "Leaf", package: "leaf"),
2124
.product(name: "Vapor", package: "vapor"),
@@ -28,9 +31,9 @@ let package = Package(
2831
.unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
2932
]
3033
),
31-
.executableTarget(name: "Run", dependencies: [.target(name: "App")]),
32-
.testTarget(name: "AppTests", dependencies: [
33-
.target(name: "App"),
34+
.executableTarget(name: "Run", dependencies: [.target(name: "ParseServerSwift")]),
35+
.testTarget(name: "ParseServerSwiftTests", dependencies: [
36+
.target(name: "ParseServerSwift"),
3437
.product(name: "XCTVapor", package: "vapor"),
3538
])
3639
]

README.md

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,136 @@
11
# ParseServerSwift
22

3-
Write Cloud Code in Swift! What is Cloud Code?
3+
[![Build Status CI](https://github.com/netreconlab/parse-server-swift/workflows/ci/badge.svg?branch=main)](https://github.com/netreconlab/parse-server-swift/actions?query=workflow%3Aci+branch%3Amain)
44

5-
For complex apps, sometimes you just need logic that isn’t running on a mobile device. Cloud Code makes this possible.
5+
Write Cloud Code in Swift!
6+
7+
What is Cloud Code? For complex apps, sometimes you just need logic that isn’t running on a mobile device. Cloud Code makes this possible.
68
Cloud Code in ParseServerSwift is easy to use because it’s built using [Parse-Swift](https://github.com/parse-community/Parse-Swift)
79
and [Vapor](https://github.com/vapor/vapor). The only difference is that this code runs in your ParseServerSwift rather than running on the user’s mobile device. When you update your Cloud Code,
810
it becomes available to all mobile environments instantly. You don’t have to wait for a new release of your application.
911
This lets you change app behavior on the fly and add new features faster.
12+
13+
## Configure ParseServerSwift
14+
To configure, you should edit [ParseServerSwift/Sources/ParseServerSwift/configure.swift](https://github.com/netreconlab/ParseServerSwift/blob/main/Sources/App/configure.swift)
15+
16+
### WebhookKey
17+
The `webhookKey` should match the [webhookKey on the Parse Server](https://github.com/parse-community/parse-server/blob/42c954318926823446326c95188b844e19954711/src/Options/Definitions.js#L491-L494). If you decide not the a `webhookKey`, set the value to `nil` in your ParseServerSwift.
18+
19+
### Hostname, Port, and TLS
20+
By default, the hostname is `127.0.0.1` and the port is `8081`. These values can easily be changed:
21+
22+
```swift
23+
app.http.server.configuration.hostname = "your.hostname.com"
24+
app.http.server.configuration.port = 8081
25+
app.http.server.configuration.tlsConfiguration = .none
26+
```
27+
28+
### Parse Swift SDK
29+
Configure the SDK as described in the [documentation](https://parseplatform.org/Parse-Swift/release/documentation/parseswift/parseswift/initialize(applicationid:clientkey:masterkey:serverurl:livequeryserverurl:allowingcustomobjectids:usingtransactions:usingequalqueryconstraint:keyvaluestore:requestcachepolicy:cachememorycapacity:cachediskcapacity:migratingfromobjcsdk:deleti-97083)).
30+
31+
```swift
32+
// Required: Change to your Parse Server serverURL.
33+
guard let parseServerUrl = URL(string: "http://localhost:1337/1") else {
34+
throw ParseError(code: .unknownError,
35+
message: "Could not make Parse Server URL")
36+
}
37+
38+
// Initialize the Parse-Swift SDK
39+
ParseSwift.initialize(applicationId: "applicationId", // Required: Change to your applicationId.
40+
clientKey: "clientKey", // Required: Change to your clientKey.
41+
masterKey: "masterKey", // Required: Change to your masterKey.
42+
serverURL: parseServerUrl) { _, completionHandler in
43+
completionHandler(.performDefaultHandling, nil)
44+
}
45+
```
46+
47+
## Adding `ParseObject`'s
48+
It is recommended to add all of your `ParseObject`'s to [ParseServerSwift/Sources/ParseServerSwift/Models](https://github.com/netreconlab/ParseServerSwift/blob/main/Sources/ParseServerSwift/Models). An example `GameScore` model is provided:
49+
50+
```swift
51+
import Foundation
52+
import ParseSwift
53+
54+
/**
55+
An example `ParseObject`. This is for testing. You can
56+
remove when creating your application.
57+
*/
58+
struct GameScore: ParseObject {
59+
// These are required by ParseObject.
60+
var objectId: String?
61+
var createdAt: Date?
62+
var updatedAt: Date?
63+
var ACL: ParseACL?
64+
var originalData: Data?
65+
66+
// Your own properties.
67+
var points: Int?
68+
69+
// Implement your own version of merge.
70+
func merge(with object: Self) throws -> Self {
71+
var updated = try mergeParse(with: object)
72+
if updated.shouldRestoreKey(\.points,
73+
original: object) {
74+
updated.points = object.points
75+
}
76+
return updated
77+
}
78+
}
79+
```
80+
81+
### The `ParseUser` Model
82+
Be sure to add all of the additional properties you have in your `_User` class to the `User` model which is located at [ParseServerSwift/Sources/ParseServerSwift/Models/User.swift](https://github.com/netreconlab/ParseServerSwift/blob/main/Sources/ParseServerSwift/Models/User.swift)
83+
84+
## Parse Cloud Code Hook Routes
85+
Adding routes for ParseHooks are as simple as adding [routes in Vapor](https://docs.vapor.codes/basics/routing/). `ParseServerSwift` adds some additional methods to routes to easily create and register [Hook Functions](https://parseplatform.org/Parse-Swift/release/documentation/parseswift/parsehookfunctionable) and [Hook Triggers](https://parseplatform.org/Parse-Swift/release/documentation/parseswift/parsehooktriggerable/). All routes should be added to [ParseServerSwift/Sources/ParseServerSwift/routes.swift](https://github.com/netreconlab/ParseServerSwift/blob/main/Sources/ParseServerSwift/routes.swift)
86+
87+
### Cloud Code Functions
88+
Cloud Code Functions can also take parameters. It's recommended to place all paramters in
89+
[ParseServerSwift/Sources/ParseServerSwift/Models/Parameters](https://github.com/netreconlab/ParseServerSwift/blob/main/Sources/ParseServerSwift/Models/Parameters)
90+
91+
```swift
92+
app.post("foo",
93+
name: "foo") { req async throws -> ParseHookResponse<String> in
94+
if let error: ParseHookResponse<String> = checkHeaders(req) {
95+
return error
96+
}
97+
var parseRequest = try req.content
98+
.decode(ParseHookFunctionRequest<User, FooParameters>.self)
99+
100+
// If a User called the request, fetch the complete user.
101+
if parseRequest.user != nil {
102+
parseRequest = try await parseRequest.hydrateUser()
103+
}
104+
105+
// To query using the User's credentials who called this function,
106+
// use the options() method from the request
107+
let options = parseRequest.options()
108+
let scores = try await GameScore.query.findAll(options: options)
109+
req.logger.info("Scores this user can access: \(scores)")
110+
return ParseHookResponse(success: "Hello, new world!")
111+
}
112+
```
113+
114+
### Cloud Code Triggers
115+
```swift
116+
app.post("bar",
117+
className: "GameScore",
118+
triggerName: .afterSave) { req async throws -> ParseHookResponse<Bool> in
119+
if let error: ParseHookResponse<Bool> = checkHeaders(req) {
120+
return error
121+
}
122+
var parseRequest = try req.content
123+
.decode(ParseHookTriggerRequest<User, GameScore>.self)
124+
125+
// If a User called the request, fetch the complete user.
126+
if parseRequest.user != nil {
127+
parseRequest = try await parseRequest.hydrateUser()
128+
}
129+
130+
// To query using the masterKey pass the `useMasterKey option
131+
// to ther query.
132+
let scores = try await GameScore.query.findAll(options: [.useMasterKey])
133+
req.logger.info("All scores: \(scores)")
134+
return ParseHookResponse(success: true)
135+
}
136+
```

Sources/App/configure.swift

Lines changed: 0 additions & 41 deletions
This file was deleted.

Sources/App/Extensions/Parse+Vapor.swift renamed to Sources/ParseServerSwift/Extensions/Parse+Vapor.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,3 @@ import ParseSwift
1111
extension ParseHookFunctionRequest: Content {}
1212
extension ParseHookTriggerRequest: Content {}
1313
extension ParseHookResponse: Content {}
14-
extension ParseFile: Content {}

Sources/App/Models/HookFunction.swift renamed to Sources/ParseServerSwift/Models/HookFunction.swift

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,23 @@ import Vapor
1313
Parse Hook Functions can be created by conforming to
1414
`ParseHookFunctionable`.
1515
*/
16-
struct HookFunction: ParseHookFunctionable {
17-
var functionName: String?
18-
var url: URL?
16+
public struct HookFunction: ParseHookFunctionable {
17+
public var functionName: String?
18+
public var url: URL?
19+
20+
public init() {}
1921
}
2022

2123
// MARK: RoutesBuilder
22-
extension RoutesBuilder {
24+
public extension RoutesBuilder {
25+
/**
26+
Creates a new route and for a Parse Cloud Code hook function.
27+
- parameter path: A variadic list of paths.
28+
- parameter name: The name of the function.
29+
- parameter url: The endpoint of the hook.
30+
*/
2331
@discardableResult
24-
public func post<Response>(
32+
func post<Response>(
2533
_ path: PathComponent...,
2634
name: String,
2735
use closure: @escaping (Request) async throws -> Response
@@ -47,8 +55,14 @@ extension RoutesBuilder {
4755
return self.post(path, use: closure)
4856
}
4957

58+
/**
59+
Creates a new route and for a Parse Cloud Code hook function.
60+
- parameter path: An array of paths.
61+
- parameter name: The name of the function.
62+
- parameter url: The endpoint of the hook.
63+
*/
5064
@discardableResult
51-
public func post<Response>(
65+
func post<Response>(
5266
_ path: [PathComponent],
5367
name: String,
5468
triggerName: ParseHookTriggerType,

Sources/App/Models/HookTrigger.swift renamed to Sources/ParseServerSwift/Models/HookTrigger.swift

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,25 @@ import Vapor
1313
Parse Hook Triggers can be created by conforming to
1414
`ParseHookFunctionable`.
1515
*/
16-
struct HookTrigger: ParseHookTriggerable {
17-
var className: String?
18-
var triggerName: ParseHookTriggerType?
19-
var url: URL?
16+
public struct HookTrigger: ParseHookTriggerable {
17+
public var className: String?
18+
public var triggerName: ParseHookTriggerType?
19+
public var url: URL?
20+
21+
public init() {}
2022
}
2123

2224
// MARK: RoutesBuilder
23-
extension RoutesBuilder {
25+
public extension RoutesBuilder {
26+
/**
27+
Creates a new route and for a Parse Cloud Code hook trigger.
28+
- parameter path: A variadic list of paths.
29+
- parameter className: The name of the `ParseObject` the trigger should act on.
30+
- parameter triggerName: The `ParseHookTriggerType` type.
31+
- parameter url: The endpoint of the hook.
32+
*/
2433
@discardableResult
25-
public func post<Response>(
34+
func post<Response>(
2635
_ path: PathComponent...,
2736
className: String,
2837
triggerName: ParseHookTriggerType,
@@ -50,8 +59,15 @@ extension RoutesBuilder {
5059
return self.post(path, use: closure)
5160
}
5261

62+
/**
63+
Creates a new route and for a Parse Cloud Code hook trigger.
64+
- parameter path: An array of paths.
65+
- parameter className: The name of the `ParseObject` the trigger should act on.
66+
- parameter triggerName: The `ParseHookTriggerType` type.
67+
- parameter url: The endpoint of the hook.
68+
*/
5369
@discardableResult
54-
public func post<Response>(
70+
func post<Response>(
5571
_ path: [PathComponent],
5672
className: String,
5773
triggerName: ParseHookTriggerType,

0 commit comments

Comments
 (0)