This is an automated email from the ASF dual-hosted git repository.
dgrove pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk-runtime-swift.git
The following commit(s) were added to refs/heads/master by this push:
new c4d08d0 Add Support for Swift 5.7 (#153)
c4d08d0 is described below
commit c4d08d09ce2afbfc8c0aa478128fa185d8868b8a
Author: Andrea Scuderi <[email protected]>
AuthorDate: Thu Jul 6 00:44:56 2023 +0100
Add Support for Swift 5.7 (#153)
---
.github/workflows/ci.yaml | 4 +
.gitignore | 3 +
.travis.yml | 2 +-
README.md | 132 +++++++-
ansible/files/runtimes.json | 14 +
core/swift57Action/CHANGELOG.md | 25 ++
core/swift57Action/Dockerfile | 66 ++++
.../swift57Action/Package.swift | 51 ++-
core/swift57Action/_Whisk.swift | 277 ++++++++++++++++
settings.gradle => core/swift57Action/build.gradle | 30 +-
core/swift57Action/main.swift | 129 ++++++++
core/swift57Action/swiftbuild.py | 111 +++++++
core/swift57Action/swiftbuild.py.launcher.swift | 359 +++++++++++++++++++++
core/swift57Action/swiftbuildandlink.sh | 18 ++
examples/swift-async-main-single/Makefile | 52 +++
examples/swift-async-main-single/main.swift | 62 ++++
.../swift-async-main-zip/HelloSwift5/Package.swift | 48 ++-
.../HelloSwift5/Sources/main.swift | 41 +--
examples/swift-async-main-zip/Makefile | 56 ++++
settings.gradle | 2 +
tests/dat/actions/Makefile | 7 +-
tests/dat/actions/SwiftyRequest5/Package.resolved | 79 -----
.../SwiftyRequestAsyncCodable57/Package.resolved | 86 +++++
.../SwiftyRequestAsyncCodable57/Package.swift | 55 ++--
.../SwiftyRequestAsyncCodable57/Sources/main.swift | 53 +++
.../actions/SwiftyRequestCodable5/Package.resolved | 79 -----
tests/dat/build.sh | 6 +
.../Swift57ActionContainerTests.scala | 145 +++++++++
.../Swift57CodableActionContainerTests.scala | 34 +-
.../test/scala/runtime/sdk/Swift57SDKTests.scala | 33 +-
tools/travis/build.sh | 1 +
tools/travis/publish.sh | 2 +
32 files changed, 1699 insertions(+), 363 deletions(-)
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index db6661e..7c0161f 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -101,6 +101,8 @@ jobs:
./gradlew :core:swift53Action:distDocker -PdockerRegistry=docker.io
-PdockerImagePrefix=openwhisk -PdockerImageTag=$SHORT_COMMIT
./gradlew :core:swift54Action:distDocker -PdockerRegistry=docker.io
-PdockerImagePrefix=openwhisk -PdockerImageTag=nightly
./gradlew :core:swift54Action:distDocker -PdockerRegistry=docker.io
-PdockerImagePrefix=openwhisk -PdockerImageTag=$SHORT_COMMIT
+ ./gradlew :core:swift57Action:distDocker -PdockerRegistry=docker.io
-PdockerImagePrefix=openwhisk -PdockerImageTag=nightly
+ ./gradlew :core:swift57Action:distDocker -PdockerRegistry=docker.io
-PdockerImagePrefix=openwhisk -PdockerImageTag=$SHORT_COMMIT
- name: Push Release Images
if: ${{ env.PUSH_RELEASE == 'true' }}
working-directory: runtime
@@ -113,5 +115,7 @@ jobs:
RUNTIME="swift53Action"
elif [ ${RUNTIME_VERSION} == "5.4" ]; then
RUNTIME="swift54Action"
+ elif [ ${RUNTIME_VERSION} == "5.7" ]; then
+ RUNTIME="swift57Action"
fi
./gradlew :core:$RUNTIME:distDocker -PdockerRegistry=docker.io
-PdockerImagePrefix=openwhisk -PdockerImageTag=$IMAGE_TAG
diff --git a/.gitignore b/.gitignore
index 9bf0539..d5be8df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -81,3 +81,6 @@ test.json
*.done
examples/*/*.zip
ActionLoop/
+
+#Swift
+**/.swiftpm/
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 85f1568..be86098 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,7 @@
#
sudo: required
-dist: xenial
+dist: jammy
jdk: openjdk8
language: java
services:
diff --git a/README.md b/README.md
index b46e800..155d418 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@
- [Swift 5.1 CHANGELOG.md](core/swift51Action/CHANGELOG.md)
- [Swift 5.3 CHANGELOG.md](core/swift53Action/CHANGELOG.md)
- [Swift 5.4 CHANGELOG.md](core/swift54Action/CHANGELOG.md)
+- [Swift 5.7 CHANGELOG.md](core/swift57Action/CHANGELOG.md)
## Quick Swift Action
### Simple swift action hello.swift
@@ -45,7 +46,7 @@ func main(args: Any) -> Any {
For the return result, not only support `dictionary`, but also support `array`
-So a very simple `hello array` function woule be:
+So a very simple `hello array` function would be:
```swift
func main(args: Any) -> Any {
@@ -63,7 +64,7 @@ So the function can be:
return args
}
```
-When invokes above action, we can pass an array object as the input parameter.
+When invoking the above action, we can pass an array object as the input
parameter.
## Swift 5.x support
@@ -85,13 +86,13 @@ func main(input: Employee, respondWith: (Employee?, Error?)
-> Void) -> Void {
respondWith(input, nil)
}
```
-```
+```bash
wsk action update helloCodableAsync helloCodableAsync.swift swift:5.1
```
-```
+```bash
ok: updated action helloCodableAsync
```
-```
+```bash
wsk action invoke helloCodableAsync -r -p id 73 -p name Andrea
```
```json
@@ -125,13 +126,13 @@ func main(input: Employee, respondWith: (Employee?,
Error?) -> Void) -> Void {
}
}
```
-```
+```bash
wsk action update helloCodableError helloCodableError.swift swift:5.1
```
-```
+```bash
ok: updated action helloCodableError
```
-```
+```bash
wsk action invoke helloCodableError -b -p id 51 -p name Carlos
```
```json
@@ -159,14 +160,125 @@ To avoid the cold-start delay, you can compile your
Swift file into a binary and
Use the docker container and pass the single source file as stdin.
Pass the name of the method to the flag `-compile`
-```
+```bash
docker run -i openwhisk/action-swift-v5.1 -compile main <main.swift
>../action.zip
```
### Compiling Swift 5.1 multiple files with dependencies
Use the docker container and pass a zip archive containing a `Package.swift`
and source files a main source file in the location `Sources/main.swift`.
-```
+```bash
zip - -r * | docker run -i openwhisk/action-swift-v5.1 -compile main
>../action.zip
```
For more build examples see [here](./examples/)
+
+# Swift 5.7
+
+In addition to previous ways of defining an action is now possible to use
throwing async/await inside the action.
+
+### Async throwing Action with Any Input and Any Output
+
+```swift
+func action(args: Any) async throws -> Any {
+ //async code sleep for 1 sec
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+
+ let newArgs = args as! [String:Any]
+ if let name = newArgs["name"] as? String {
+ return [ "greeting" : "Hello \(name)!" ]
+ } else {
+ return [ "greeting" : "Hello stranger!" ]
+ }
+}
+```
+
+### Async throwing Action with a Codable Input and a Codable Output
+
+```swift
+struct Input: Codable {
+ let name: String?
+}
+
+struct Output: Codable {
+ let count: Int
+}
+
+func action(input: Input) async throws -> Output? {
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+ if let name = input.name {
+ return Output(count: name.count)
+ } else {
+ return Output(count: 0)
+ }
+}
+```
+
+### Async throwing Action with Codable Output
+
+```swift
+struct Input: Codable {
+ let name: String?
+}
+
+struct Output: Codable {
+ let count: Int
+}
+
+func action() async throws -> Output? {
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+ return Output(count: 0)
+}
+```
+
+
+### Example of an async throwing Action with a Codable Input and a Codable
Output
+
+In the following example, the main action decodes the URL from `AnInput`,
downloads the content from the URL, decodes the JSON and returns the `response`
in `AnOutput`.
+In case of failure, the runtime will return an error.
+
+```swift
+import AsyncHTTPClient
+import Foundation
+import _Concurrency
+import NIOCore
+import NIOFoundationCompat
+
+enum RequestError: Error {
+ case requestError
+}
+struct AnInput: Codable {
+ let url: String?
+}
+
+struct AnOutput: Codable {
+ let args: [String: String]
+ let headers: [String: String]
+ let origin: String
+ let url: String
+}
+
+let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
+let decoder = JSONDecoder()
+
+func main(param: AnInput) async throws -> AnOutput {
+
+ let echoURL = param.url ?? "https://httpbin.org/get"
+ let request = HTTPClientRequest(url: echoURL)
+ let response = try await httpClient.execute(request, timeout: .seconds(3))
+ if response.status == .ok {
+ let bytes = try await response.body.collect(upTo: 1024 * 1024) // 1 MB
Buffer
+ let data = Data(buffer: bytes)
+ return try decoder.decode(AnOutput.self, from: data)
+ } else {
+ throw RequestError.requestError
+ }
+}
+```
+
+The full swift package is
[here](/tests/dat/actions/SwiftyRequestAsyncCodable57/).
+
+Note that the package of this action contains a dependency from
`AsynHTTPClient`, in such case, it's preferred to build the action.
+
+```shell
+zip - -r * | docker run -i openwhisk/action-swift-v5.7 -compile main
>../action.zip
+```
diff --git a/ansible/files/runtimes.json b/ansible/files/runtimes.json
index bbc4958..ed5cbe1 100644
--- a/ansible/files/runtimes.json
+++ b/ansible/files/runtimes.json
@@ -71,6 +71,20 @@
"attachmentName": "codefile",
"attachmentType": "text/plain"
}
+ },
+ {
+ "kind": "swift:5.7",
+ "default": false,
+ "image": {
+ "prefix": "testing",
+ "name": "action-swift-v5.7",
+ "tag": "latest"
+ },
+ "deprecated": false,
+ "attached": {
+ "attachmentName": "codefile",
+ "attachmentType": "text/plain"
+ }
}
]
},
diff --git a/core/swift57Action/CHANGELOG.md b/core/swift57Action/CHANGELOG.md
new file mode 100644
index 0000000..45636dc
--- /dev/null
+++ b/core/swift57Action/CHANGELOG.md
@@ -0,0 +1,25 @@
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+# Apache OpenWhisk Swift 5.7 Runtime Container
+
+- Support for swift async/await
+
+## 1.17.0
+ - Next Apache Release
diff --git a/core/swift57Action/Dockerfile b/core/swift57Action/Dockerfile
new file mode 100644
index 0000000..cefe98d
--- /dev/null
+++ b/core/swift57Action/Dockerfile
@@ -0,0 +1,66 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# build go proxy from source
+FROM golang:1.18 AS builder_source
+ARG GO_PROXY_GITHUB_USER=apache
+ARG GO_PROXY_GITHUB_BRANCH=master
+RUN git clone --branch ${GO_PROXY_GITHUB_BRANCH} \
+ https://github.com/${GO_PROXY_GITHUB_USER}/openwhisk-runtime-go /src ; \
+ cd /src ; env GO111MODULE=on CGO_ENABLED=0 go build main/proxy.go && \
+ mv proxy /bin/proxy
+
+# or build it from a release
+FROM golang:1.18 AS builder_release
+ARG [email protected]
+RUN curl -sL
https://github.com/apache/openwhisk-runtime-go/archive/{$GO_PROXY_RELEASE_VERSION}.tar.gz
| tar xzf - \
+ && cd openwhisk-runtime-go-*/main \
+ && GO111MODULE=on CGO_ENABLED=0 go build -o /bin/proxy
+
+FROM swift:5.7.2
+
+# select the builder to use
+ARG GO_PROXY_BUILD_FROM=release
+
+RUN rm -rf /var/lib/apt/lists/* \
+ && apt-get clean && apt-get -qq update \
+ && apt-get install -y --no-install-recommends locales python3 vim
libssl-dev libicu-dev \
+ && rm -rf /var/lib/apt/lists/* \
+ && locale-gen en_US.UTF-8
+
+ENV LANG="en_US.UTF-8" \
+ LANGUAGE="en_US:en" \
+ LC_ALL="en_US.UTF-8"
+
+RUN mkdir -p /swiftAction
+WORKDIR /swiftAction
+
+COPY --from=builder_source /bin/proxy /bin/proxy_source
+COPY --from=builder_release /bin/proxy /bin/proxy_release
+RUN mv /bin/proxy_${GO_PROXY_BUILD_FROM} /bin/proxy
+ADD swiftbuild.py /bin/compile
+ADD swiftbuild.py.launcher.swift /bin/compile.launcher.swift
+COPY _Whisk.swift /swiftAction/Sources/
+COPY Package.swift /swiftAction/
+COPY swiftbuildandlink.sh /swiftAction/
+COPY main.swift /swiftAction/Sources/
+RUN swift build -c release; \
+ touch /swiftAction/Sources/main.swift; \
+ rm /swiftAction/.build/release/Action
+
+ENV OW_COMPILER=/bin/compile
+ENTRYPOINT [ "/bin/proxy" ]
diff --git a/settings.gradle b/core/swift57Action/Package.swift
similarity index 58%
copy from settings.gradle
copy to core/swift57Action/Package.swift
index 8079687..1d48af9 100644
--- a/settings.gradle
+++ b/core/swift57Action/Package.swift
@@ -1,3 +1,6 @@
+// swift-tools-version:5.7
+// The swift-tools-version declares the minimum version of Swift required to
build this package.
+
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -15,31 +18,23 @@
* limitations under the License.
*/
-include 'tests'
-
-include 'core:swift51Action'
-
-include 'core:swift53Action'
-
-include 'core:swift54Action'
-
-rootProject.name = 'runtime-swift'
-
-gradle.ext.openwhisk = [
- version: '1.0.1-SNAPSHOT'
-]
-
-gradle.ext.scala = [
- version: '2.12.7',
- depVersion : '2.12',
- compileFlags: ['-feature', '-unchecked', '-deprecation',
'-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
- version: '1.5.0',
- config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
-
-gradle.ext.akka = [version : '2.6.12']
-gradle.ext.akka_http = [version : '10.2.4']
-
+import PackageDescription
+
+let package = Package(
+ name: "Action",
+ platforms: [
+ .macOS(.v12),
+ ],
+ products: [
+ .executable(
+ name: "Action",
+ targets: ["Action"]
+ )
+ ],
+ targets: [
+ .executableTarget(
+ name: "Action",
+ path: "."
+ )
+ ]
+)
diff --git a/core/swift57Action/_Whisk.swift b/core/swift57Action/_Whisk.swift
new file mode 100644
index 0000000..3cade25
--- /dev/null
+++ b/core/swift57Action/_Whisk.swift
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Foundation
+import Dispatch
+#if canImport(FoundationNetworking)
+import FoundationNetworking
+#endif
+
+class Whisk {
+
+ static var baseUrl = ProcessInfo.processInfo.environment["__OW_API_HOST"]
+ static var apiKey = ProcessInfo.processInfo.environment["__OW_API_KEY"]
+ // This will allow user to modify the default JSONDecoder and JSONEncoder
used by epilogue
+ static var jsonDecoder = JSONDecoder()
+ static var jsonEncoder = JSONEncoder()
+
+ class func invoke(actionNamed action: String, withParameters params:
[String: Any], blocking: Bool = true) -> [String: Any] {
+ let parsedAction = parseQualifiedName(name: action)
+ let strBlocking = blocking ? "true" : "false"
+ let path =
"/api/v1/namespaces/\(parsedAction.namespace)/actions/\(parsedAction.name)?blocking=\(strBlocking)"
+ return sendWhiskRequestSyncronish(uriPath: path, params: params,
method: "POST")
+ }
+
+ class func trigger(eventNamed event : String, withParameters params:
[String: Any]) -> [String: Any] {
+ let parsedEvent = parseQualifiedName(name: event)
+ let path =
"/api/v1/namespaces/\(parsedEvent.namespace)/triggers/\(parsedEvent.name)?blocking=true"
+ return sendWhiskRequestSyncronish(uriPath: path, params: params,
method: "POST")
+ }
+
+ class func createTrigger(triggerNamed trigger: String, withParameters
params: [String:Any]) -> [String: Any] {
+ let parsedTrigger = parseQualifiedName(name: trigger)
+ let path =
"/api/v1/namespaces/\(parsedTrigger.namespace)/triggers/\(parsedTrigger.name)"
+ return sendWhiskRequestSyncronish(uriPath: path, params: params,
method: "PUT")
+ }
+
+ class func createRule(ruleNamed ruleName: String, withTrigger triggerName:
String, andAction actionName: String) -> [String: Any] {
+ let parsedRule = parseQualifiedName(name: ruleName)
+ let path =
"/api/v1/namespaces/\(parsedRule.namespace)/rules/\(parsedRule.name)"
+ let params = ["trigger":triggerName, "action":actionName]
+ return sendWhiskRequestSyncronish(uriPath: path, params: params,
method: "PUT")
+ }
+
+ // handle the GCD dance to make the post async, but then obtain/return
+ // the result from this function sync
+ private class func sendWhiskRequestSyncronish(uriPath path: String,
params: [String: Any], method: String) -> [String: Any] {
+ var response : [String: Any]!
+
+ let queue = DispatchQueue.global()
+ let invokeGroup = DispatchGroup()
+
+ invokeGroup.enter()
+ queue.async {
+ postUrlSession(uriPath: path, params: params, method: method,
group: invokeGroup) { result in
+ response = result
+ }
+ }
+
+ // On one hand, FOREVER seems like an awfully long time...
+ // But on the other hand, I think we can rely on the system to kill
this
+ // if it exceeds a reasonable execution time.
+ switch invokeGroup.wait(timeout: DispatchTime.distantFuture) {
+ case DispatchTimeoutResult.success:
+ break
+ case DispatchTimeoutResult.timedOut:
+ break
+ }
+
+ return response
+ }
+
+
+ /**
+ * Using new UrlSession
+ */
+ private class func postUrlSession(uriPath: String, params : [String:Any],
method: String,group: DispatchGroup, callback : @escaping([String:Any]) ->
Void) {
+
+ guard let encodedPath =
uriPath.addingPercentEncoding(withAllowedCharacters:
CharacterSet.urlQueryAllowed) else {
+ callback(WhiskError.invalidURIPath.body)
+ return
+ }
+
+ let urlStr = "\(baseUrl!)\(encodedPath)"
+ if let url = URL(string: urlStr) {
+ var request = URLRequest(url: url)
+ request.httpMethod = method
+
+ do {
+ request.addValue("application/json", forHTTPHeaderField:
"Content-Type")
+ request.httpBody = try JSONSerialization.data(withJSONObject:
params)
+
+ let loginData: Data = apiKey!.data(using:
String.Encoding.utf8, allowLossyConversion: false)!
+ let base64EncodedAuthKey =
loginData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue:
0))
+ request.addValue("Basic \(base64EncodedAuthKey)",
forHTTPHeaderField: "Authorization")
+ let session = URLSession(configuration:
URLSessionConfiguration.default)
+
+ let task = session.dataTask(with: request, completionHandler:
{data, response, error -> Void in
+
+ // exit group after we are done
+ defer {
+ group.leave()
+ }
+
+ if let error = error {
+ callback(WhiskError.generic(error).body)
+ } else {
+ if let data = data {
+ let decodeReponse = decodeWhiskResponse(data: data)
+ callback(decodeReponse)
+ }
+ }
+ })
+
+ task.resume()
+ } catch {
+ callback(WhiskError.invalidParams(error).body)
+ }
+ }
+ }
+
+ static func invoke(actionNamed action: String, withParameters params:
[String: Any], blocking: Bool = true) async throws -> Data {
+ let parsedAction = parseQualifiedName(name: action)
+ let strBlocking = blocking ? "true" : "false"
+ let path =
"/api/v1/namespaces/\(parsedAction.namespace)/actions/\(parsedAction.name)?blocking=\(strBlocking)"
+ return try await postUrlSession(uriPath: path, params: params, method:
"POST")
+ }
+
+ static func trigger(eventNamed event: String, withParameters params:
[String: Any]) async throws -> Data {
+ let parsedEvent = parseQualifiedName(name: event)
+ let path =
"/api/v1/namespaces/\(parsedEvent.namespace)/triggers/\(parsedEvent.name)?blocking=true"
+ return try await postUrlSession(uriPath: path, params: params, method:
"POST")
+ }
+
+ static func createTrigger(triggerNamed trigger: String, withParameters
params : [String:Any]) async throws -> Data {
+ let parsedTrigger = parseQualifiedName(name: trigger)
+ let path =
"/api/v1/namespaces/\(parsedTrigger.namespace)/triggers/\(parsedTrigger.name)"
+ return try await postUrlSession(uriPath: path, params: params, method:
"PUT")
+ }
+
+ static func createRule(ruleNamed ruleName: String, withTrigger
triggerName: String, andAction actionName: String) async throws -> Data {
+ let parsedRule = parseQualifiedName(name: ruleName)
+ let path =
"/api/v1/namespaces/\(parsedRule.namespace)/rules/\(parsedRule.name)"
+ let params = ["trigger":triggerName, "action":actionName]
+ return try await postUrlSession(uriPath: path, params: params, method:
"PUT")
+ }
+
+ private static func postUrlSession(uriPath: String, params : [String:Any],
method: String) async throws -> Data {
+
+ guard let encodedPath =
uriPath.addingPercentEncoding(withAllowedCharacters:
CharacterSet.urlQueryAllowed) else {
+ throw WhiskError.invalidURIPath
+ }
+ guard let baseUrl = baseUrl,
+ let url = URL(string: "\(baseUrl)\(encodedPath)") else {
+ throw WhiskError.invalidURL
+ }
+ var request = URLRequest(url: url)
+ request.httpMethod = method
+ request.addValue("application/json", forHTTPHeaderField:
"Content-Type")
+
+ do {
+ request.httpBody = try JSONSerialization.data(withJSONObject:
params)
+ } catch {
+ throw WhiskError.invalidParams(error)
+ }
+
+ guard let loginData: Data = apiKey?.data(using: String.Encoding.utf8,
allowLossyConversion: false) else {
+ throw WhiskError.invalidLogin
+ }
+ let base64EncodedAuthKey = loginData.base64EncodedString(options:
NSData.Base64EncodingOptions(rawValue: 0))
+ request.addValue("Basic \(base64EncodedAuthKey)", forHTTPHeaderField:
"Authorization")
+ let session = URLSession(configuration:
URLSessionConfiguration.default)
+ return try await session.asyncWhiskData(with: request)
+ }
+
+ static func decodeWhiskResponse(data: Data) -> [String: Any] {
+ do {
+ let respJson = try JSONSerialization.jsonObject(with: data)
+ if let respJson = respJson as? [String:Any] {
+ return respJson
+ } else {
+ return WhiskError.jsonIsNotDictionary.body
+ }
+ } catch {
+ return WhiskError.invalidJSON(error).body
+ }
+ }
+
+ // separate an OpenWhisk qualified name (e.g. "/whisk.system/samples/date")
+ // into namespace and name components
+ private class func parseQualifiedName(name qualifiedName : String) ->
(namespace : String, name : String) {
+ let defaultNamespace = "_"
+ let delimiter = "/"
+
+ let segments :[String] = qualifiedName.components(separatedBy:
delimiter)
+
+ if segments.count > 2 {
+ return (segments[1],
Array(segments[2..<segments.count]).joined(separator: delimiter))
+ } else if segments.count == 2 {
+ // case "/action" or "package/action"
+ let name = qualifiedName.hasPrefix(delimiter) ? segments[1] :
segments.joined(separator: delimiter)
+ return (defaultNamespace, name)
+ } else {
+ return (defaultNamespace, segments[0])
+ }
+ }
+}
+
+enum WhiskError: LocalizedError {
+ case invalidURIPath
+ case generic(Error)
+ case invalidURL
+ case invalidLogin
+ case invalidJSON(Error)
+ case invalidParams(Error)
+ case jsonIsNotDictionary
+ case noData
+
+ var errorDescription: String {
+ switch self {
+ case .noData:
+ return ""
+ case .generic(let error):
+ return error.localizedDescription
+ case .invalidURL:
+ return "Invalid URL"
+ case .invalidJSON(let error):
+ return "Error creating json from response: \(error)"
+ case .jsonIsNotDictionary:
+ return " response from server is not a dictionary"
+ case .invalidURIPath:
+ return "Error encoding uri path to make openwhisk REST call."
+ case .invalidParams(let error):
+ return "Got error creating params body: \(error)"
+ case .invalidLogin:
+ return "Invalid __OW_API_KEY"
+ }
+ }
+
+ var body: [String: String] {
+ return ["error": errorDescription]
+ }
+}
+
+extension URLSession {
+
+ // The async version of it, it's not supported
+ // See:
https://github.com/apple/swift-corelibs-foundation/blob/main/Docs/Status.md#entities
+
+ func asyncWhiskData(with request: URLRequest) async throws -> Data {
+ let taskResult = await withCheckedContinuation { continuation in
+ self.dataTask(with: request) { data, response, error in
+ continuation.resume(returning: (data, response, error))
+ }.resume()
+ }
+ if let error = taskResult.2 {
+ throw WhiskError.generic(error)
+ }
+ guard let data = taskResult.0 else {
+ throw WhiskError.noData
+ }
+ return data
+ }
+}
diff --git a/settings.gradle b/core/swift57Action/build.gradle
similarity index 58%
copy from settings.gradle
copy to core/swift57Action/build.gradle
index 8079687..054558d 100644
--- a/settings.gradle
+++ b/core/swift57Action/build.gradle
@@ -15,31 +15,5 @@
* limitations under the License.
*/
-include 'tests'
-
-include 'core:swift51Action'
-
-include 'core:swift53Action'
-
-include 'core:swift54Action'
-
-rootProject.name = 'runtime-swift'
-
-gradle.ext.openwhisk = [
- version: '1.0.1-SNAPSHOT'
-]
-
-gradle.ext.scala = [
- version: '2.12.7',
- depVersion : '2.12',
- compileFlags: ['-feature', '-unchecked', '-deprecation',
'-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
- version: '1.5.0',
- config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
-
-gradle.ext.akka = [version : '2.6.12']
-gradle.ext.akka_http = [version : '10.2.4']
-
+ext.dockerImageName = 'action-swift-v5.7'
+apply from: '../../gradle/docker.gradle'
diff --git a/core/swift57Action/main.swift b/core/swift57Action/main.swift
new file mode 100644
index 0000000..603c696
--- /dev/null
+++ b/core/swift57Action/main.swift
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Foundation
+
+enum MainActionError: LocalizedError {
+ case invalidArgs
+ var errorDescription: String? {
+ switch self {
+ case .invalidArgs:
+ return "Invalid arguments"
+ }
+ }
+}
+
+func main(args: Any) async throws -> Any {
+
+ //async code sleep for 1 microsecond
+ try await Task.sleep(nanoseconds: 1_000)
+
+ guard let newArgs = args as? [String:Any] else {
+ throw MainActionError.invalidArgs
+ }
+ if let name = newArgs["name"] as? String {
+ return [ "greeting" : "Hello \(name)!" ]
+ } else {
+ return [ "greeting" : "Hello stranger!" ]
+ }
+}
+
+
+/* Examples of Actions supported by Swift 5.7
+
+// Action with Any Input and Any Output
+func main(args: Any) -> Any {
+ let newArgs = args as! [String:Any]
+ if let name = newArgs["name"] as? String {
+ return [ "greeting" : "Hello \(name)!" ]
+ } else {
+ return [ "greeting" : "Hello stranger!" ]
+ }
+}
+
+// Async Action with Any Input and Any Output
+func mainAsync(args: Any) async -> Any {
+ do {
+ //async code sleep for 1 sec
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+
+ let newArgs = args as! [String:Any]
+ if let name = newArgs["name"] as? String {
+ return [ "greeting" : "Hello \(name)!" ]
+ } else {
+ return [ "greeting" : "Hello stranger!" ]
+ }
+ } catch {
+ return ["error" : error.localizedDescription]
+ }
+}
+
+// Async throwing Action with Any Input and Any Output
+func mainAsyncThrows(args: Any) async throws -> Any {
+ //async code sleep for 1 sec
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+
+ let newArgs = args as! [String:Any]
+ if let name = newArgs["name"] as? String {
+ return [ "greeting" : "Hello \(name)!" ]
+ } else {
+ return [ "greeting" : "Hello stranger!" ]
+ }
+}
+
+struct Input: Codable {
+ let name: String?
+}
+
+struct Output: Codable {
+ let count: Int
+}
+
+// Action with Codable Input and completion with Codable Output and Error
+func mainCompletionCodable(input: Input, completion: @escaping (Output?,
Error?) -> Void) -> Void {
+ if let name = input.name {
+ let output = Output(count: name.count)
+ completion(output, nil)
+ } else {
+ let output = Output(count: 0)
+ completion(output, nil)
+ }
+}
+
+// Action with Codable Input and completion with Codable Output and Error
+func mainCompletionCodableNoInput(completion: @escaping (Output?, Error?) ->
Void) -> Void {
+ let output = Output(count: 0)
+ completion(output, nil)
+}
+
+// Async throwing Action with Codable Output
+func mainCodableAsyncThrowsNoInput() async throws -> Output? {
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+ return Output(count: 0)
+}
+
+// Async throwing Action with a Codable Input and a Codable Output
+func mainCodableAsyncThrows(input: Input) async throws -> Output? {
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+ if let name = input.name {
+ return Output(count: name.count)
+ } else {
+ return Output(count: 0)
+ }
+}
+
+*/
diff --git a/core/swift57Action/swiftbuild.py b/core/swift57Action/swiftbuild.py
new file mode 100755
index 0000000..775381c
--- /dev/null
+++ b/core/swift57Action/swiftbuild.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python3
+"""Swift Action Compiler
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""
+
+from __future__ import print_function
+import os
+import re
+import sys
+import codecs
+import subprocess
+from io import StringIO
+
+def eprint(*args, **kwargs):
+ print(*args, file=sys.stderr, **kwargs)
+
+def sources(launcher, source_dir, main):
+ actiondir = "%s/Sources" % source_dir
+ # copy the launcher fixing the main
+ dst = "%s/main.swift" % actiondir
+ with codecs.open(dst, 'a', 'utf-8') as d:
+ with codecs.open(launcher, 'r', 'utf-8') as e:
+ code = e.read()
+ code += "try await _WhiskRuntime.wiskRunLoop { jsonData in\n"
+ code += " await _WhiskRuntime.runAsyncMain(mainFunction: %s,
json: jsonData)\n" % main
+ code += "}\n"
+ d.write(code)
+
+def swift_build(dir, buildcmd):
+ # compile...
+ env = {
+ "PATH": os.environ["PATH"]
+ }
+ p = subprocess.Popen(buildcmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ cwd=dir,
+ env=env)
+ (o, e) = p.communicate()
+ # stdout/stderr may be either text or bytes, depending on Python
+ # version, so if bytes, decode to text. Note that in Python 2
+ # a string will match both types; so also skip decoding in that case
+ if isinstance(o, bytes) and not isinstance(o, str):
+ o = o.decode('utf-8')
+ if isinstance(e, bytes) and not isinstance(e, str):
+ e = e.decode('utf-8')
+ return p.returncode, o, e
+
+def build(source_dir, target_file, buildcmd):
+ r, o, e = swift_build(source_dir, buildcmd)
+ #if e: print(e)
+ #if o: print(o)
+ if r != 0:
+ print(e)
+ print(o)
+ print(r)
+ return
+
+ bin_file = "%s/.build/release/Action" % source_dir
+ os.rename(bin_file, target_file)
+ if not os.path.isfile(target_file):
+ print("failed %s -> %s" % (bin_file, target_file))
+ return
+
+
+def main(argv):
+ if len(argv) < 4:
+ print("usage: <main-function> <source-dir> <target-dir>")
+ sys.exit(1)
+
+ main = argv[1]
+ source_dir = os.path.abspath(argv[2])
+ target = os.path.abspath("%s/exec" % argv[3])
+ launch = os.path.abspath(argv[0]+".launcher.swift")
+
+ src = "%s/exec" % source_dir
+
+ #check if single source
+ if os.path.isfile(src):
+ actiondir = os.path.abspath("Sources")
+ if not os.path.isdir(actiondir):
+ os.makedirs(actiondir, mode=0o755)
+ dst = "%s/main.swift" % actiondir
+ os.rename(src, dst)
+ sources(launch, os.path.abspath("."), main)
+ build(os.path.abspath("."), target, ["./swiftbuildandlink.sh"])
+ else:
+ actiondir = "%s/Sources" % source_dir
+ if not os.path.isdir(actiondir):
+ os.makedirs(actiondir, mode=0o755)
+
os.rename(os.path.abspath("Sources/_Whisk.swift"),"%s/Sources/_Whisk.swift" %
source_dir)
+ sources(launch, source_dir, main)
+ build(source_dir, target, ["swift", "build", "-c", "release"])
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/core/swift57Action/swiftbuild.py.launcher.swift
b/core/swift57Action/swiftbuild.py.launcher.swift
new file mode 100644
index 0000000..79f395e
--- /dev/null
+++ b/core/swift57Action/swiftbuild.py.launcher.swift
@@ -0,0 +1,359 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Imports
+import Foundation
+import _Concurrency
+#if os(Linux)
+import Glibc
+#else
+import Darwin
+#endif
+
+public struct _WhiskRuntime {
+
+ private enum WhiskRuntimeErrorMessage: String {
+ case actionHandlerCallbackError = "Action handler callback returned an
error:"
+ case actionHandlerCallbackNullOrError = "Action handler callback did
not return response or error."
+ case failToEncodeDictionary = "Failed to encode Dictionary type to
JSON string:"
+ case failToEncodeCodableToJson = "JSONEncoder failed to encode Codable
type to JSON string:"
+ case errorSerializingJSON = "Error serializing JSON, data does not
appear to be valid JSON"
+ case failedToExecuteActionHandler = "Failed to execute action handler
with error:"
+ }
+
+ public static func wiskRunLoop(actionMain: ((Data) async -> Void)) async
throws {
+ while let inputStr: String = readLine() {
+ let json = inputStr.data(using: .utf8, allowLossyConversion: true)!
+ let parsed = try JSONSerialization.jsonObject(with: json, options:
[]) as! [String: Any]
+ for (key, value) in parsed {
+ if key != "value" {
+ setenv("__OW_\(key.uppercased())",value as! String,1)
+ }
+ }
+ let jsonData = try JSONSerialization.data(withJSONObject:
parsed["value"] as Any, options: [])
+ await actionMain(jsonData)
+ }
+ }
+
+ private static func whiskPrintJSONDecoderError(json: Data, error: Error?) {
+ let jsonString = String(
+ data: json,
+ encoding: .utf8
+ ) ?? ""
+ let fixedJSONString = jsonString.replacingOccurrences(of: "\"", with:
"\\\"")
+
+ let message = "JSONDecoder failed to decode JSON string
\(fixedJSONString) to Codable type:"
+ var errStr = "{\"error\":\"\(message)\"}\n"
+ if let error = error {
+ errStr = "{\"error\":\"\(message)
\(error.localizedDescription)\"\n}"
+ }
+ whiskPrintBuffer(jsonString: errStr)
+ }
+
+ private static func whiskPrintError(message: WhiskRuntimeErrorMessage,
error: Error?){
+ var errStr = "{\"error\":\"\(message.rawValue)\"}\n"
+ if let error = error {
+ errStr = "{\"error\":\"\(message.rawValue)
\(error.localizedDescription)\"\n}"
+ }
+ whiskPrintBuffer(jsonString: errStr)
+ }
+
+ private static func whiskPrintResult(jsonData: Data){
+ let jsonString = String(data: jsonData, encoding: .utf8)!
+ whiskPrintBuffer(jsonString: jsonString)
+ }
+
+ private static func whiskPrintBuffer(jsonString: String){
+ var buf : [UInt8] = Array(jsonString.utf8)
+ buf.append(10)
+ fflush(stdout)
+ fflush(stderr)
+ write(3, buf, buf.count)
+ }
+
+ /**
+ Execute an async throwing Action with Any Input and Any Output
+
+ Example:
+
+ ```
+ func action(args: Any) async throws -> Any {
+ //async code sleep for 1 sec
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+
+ let newArgs = args as! [String:Any]
+ if let name = newArgs["name"] as? String {
+ return [ "greeting" : "Hello \(name)!" ]
+ } else {
+ return [ "greeting" : "Hello stranger!" ]
+ }
+ }
+ ```
+
+ - Parameters:
+ - mainFunction: action
+ - json: action parameters
+ - Returns: Void
+ */
+ public static func runAsyncMain(mainFunction: (Any) async throws -> Any,
json: Data) async -> Void {
+ do {
+ let parsed = try JSONSerialization.jsonObject(with: json, options:
[])
+ let result = try await mainFunction(parsed)
+ if JSONSerialization.isValidJSONObject(result) {
+ do {
+ let jsonData = try JSONSerialization.data(withJSONObject:
result, options: [])
+ whiskPrintResult(jsonData: jsonData)
+ } catch {
+ whiskPrintError(message: .failToEncodeDictionary, error:
error)
+ }
+ } else {
+ whiskPrintError(message: .errorSerializingJSON, error: nil)
+ }
+ } catch let error as DecodingError {
+ whiskPrintJSONDecoderError(json: json, error: error)
+ return
+ } catch {
+ whiskPrintError(message: .failedToExecuteActionHandler, error:
error)
+ return
+ }
+ }
+
+ /**
+ Execute an Action with Codable Input and completion with Codable Output
and Error
+
+ Example:
+
+ ```
+ struct Input: Codable {
+ let name: String?
+ }
+
+ struct Output: Codable {
+ let count: Int
+ }
+
+ func action(input: Input, completion: @escaping (Output?, Error?) ->
Void) -> Void {
+ if let name = input.name {
+ let output = Output(count: name.count)
+ completion(output, nil)
+ } else {
+ let output = Output(count: 0)
+ completion(output, nil)
+ }
+ }
+ ```
+
+ - Parameters:
+ - mainFunction: action
+ - json: action parameters
+ - Returns: Void
+ */
+ public static func runAsyncMain<In: Decodable, Out:
Encodable>(mainFunction: (In, @escaping (Out?, Error?) -> Void) -> Void, json:
Data) {
+ do {
+ let input = try Whisk.jsonDecoder.decode(In.self, from: json)
+ let resultHandler = { (out: Out?, error: Error?) in
+ if let error = error {
+ whiskPrintError(message: .actionHandlerCallbackError,
error: error)
+ return
+ }
+ guard let out = out else {
+ whiskPrintError(message:
.actionHandlerCallbackNullOrError, error: nil)
+ return
+ }
+ do {
+ let jsonData = try Whisk.jsonEncoder.encode(out)
+ whiskPrintResult(jsonData: jsonData)
+ } catch let error as EncodingError {
+ whiskPrintError(message: .failToEncodeCodableToJson,
error: error)
+ return
+ } catch {
+ whiskPrintError(message: .failedToExecuteActionHandler,
error: error)
+ return
+ }
+ }
+ let _ = mainFunction(input, resultHandler)
+ } catch let error as DecodingError {
+ whiskPrintJSONDecoderError(json: json, error: error)
+ return
+ } catch {
+ whiskPrintError(message: .failedToExecuteActionHandler, error:
error)
+ return
+ }
+ }
+
+ /**
+ Execute an async throwing Action with a Codable Input and a Codable Output
+
+ Example:
+
+ ```
+ struct Input: Codable {
+ let name: String?
+ }
+
+ struct Output: Codable {
+ let count: Int
+ }
+
+ func action(input: Input) async throws -> Output? {
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+ if let name = input.name {
+ return Output(count: name.count)
+ } else {
+ return Output(count: 0)
+ }
+ }
+ ```
+
+ - Parameters:
+ - mainFunction: action
+ - json: action parameters
+ - Returns: Void
+ */
+ public static func runAsyncMain<In: Decodable, Out:
Encodable>(mainFunction: (In) async throws -> Out?, json: Data) async {
+ do {
+ let input = try Whisk.jsonDecoder.decode(In.self, from: json)
+ do {
+ let out = try await mainFunction(input)
+ guard let out = out else {
+ whiskPrintError(message:
.actionHandlerCallbackNullOrError, error: nil)
+ return
+ }
+ do {
+ let jsonData = try Whisk.jsonEncoder.encode(out)
+ whiskPrintResult(jsonData: jsonData)
+ } catch let error as EncodingError {
+ whiskPrintError(message: .failToEncodeCodableToJson,
error: error)
+ return
+ } catch {
+ whiskPrintError(message: .failedToExecuteActionHandler,
error: error)
+ return
+ }
+ } catch {
+ whiskPrintError(message: .actionHandlerCallbackError, error:
error)
+ return
+ }
+ } catch let error as DecodingError {
+ whiskPrintJSONDecoderError(json: json, error: error)
+ return
+ } catch {
+ whiskPrintError(message: .failedToExecuteActionHandler, error:
error)
+ return
+ }
+ }
+
+ /**
+ Execute an Action with Codable Input and completion with Codable Output
and Error
+
+ Example:
+
+ ```
+ struct Input: Codable {
+ let name: String?
+ }
+
+ struct Output: Codable {
+ let count: Int
+ }
+
+ func action(completion: @escaping (Output?, Error?) -> Void) -> Void {
+ let output = Output(count: 0)
+ completion(output, nil)
+ }
+ ```
+
+ - Parameters:
+ - mainFunction: action
+ - json: action parameters
+ - Returns: Void
+ */
+ public static func runAsyncMain<Out: Encodable>(mainFunction: ( @escaping
(Out?, Error?) -> Void) -> Void, json: Data) {
+ let resultHandler = { (out: Out?, error: Error?) in
+ if let error = error {
+ whiskPrintError(message: .actionHandlerCallbackError, error:
error)
+ return
+ }
+ guard let out = out else {
+ whiskPrintError(message: .actionHandlerCallbackNullOrError,
error: nil)
+ return
+ }
+ do {
+ let jsonData = try Whisk.jsonEncoder.encode(out)
+ whiskPrintResult(jsonData: jsonData)
+ } catch let error as EncodingError {
+ whiskPrintError(message: .failToEncodeCodableToJson, error:
error)
+ return
+ } catch {
+ whiskPrintError(message: .failedToExecuteActionHandler, error:
error)
+ return
+ }
+ }
+ let _ = mainFunction(resultHandler)
+ }
+
+ /**
+ Execute an async throwing Action with Codable Output
+
+ Example:
+
+ ```
+ struct Input: Codable {
+ let name: String?
+ }
+
+ struct Output: Codable {
+ let count: Int
+ }
+
+ func action() async throws -> Output? {
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+ return Output(count: 0)
+ }
+ ```
+
+ - Parameters:
+ - mainFunction: action
+ - json: action parameters
+ - Returns: Void
+ */
+ public static func runAsyncMain<Out: Encodable>(mainFunction: () async
throws -> Out?, json: Data) async {
+ do {
+ let out = try await mainFunction()
+ guard let out = out else {
+ whiskPrintError(message: .actionHandlerCallbackNullOrError,
error: nil)
+ return
+ }
+ do {
+ let jsonData = try Whisk.jsonEncoder.encode(out)
+ whiskPrintResult(jsonData: jsonData)
+ } catch let error as EncodingError {
+ whiskPrintError(message: .failToEncodeCodableToJson, error:
error)
+ return
+ } catch {
+ whiskPrintError(message: .failedToExecuteActionHandler, error:
error)
+ return
+ }
+ } catch {
+ whiskPrintError(message: .actionHandlerCallbackError, error: error)
+ return
+ }
+ }
+}
+
+
+
+
diff --git a/core/swift57Action/swiftbuildandlink.sh
b/core/swift57Action/swiftbuildandlink.sh
new file mode 100755
index 0000000..c35a1ba
--- /dev/null
+++ b/core/swift57Action/swiftbuildandlink.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+swift build -c release
diff --git a/examples/swift-async-main-single/Makefile
b/examples/swift-async-main-single/Makefile
new file mode 100644
index 0000000..0a9ac82
--- /dev/null
+++ b/examples/swift-async-main-single/Makefile
@@ -0,0 +1,52 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+OW_USER?=whisk
+OW_RUNTIME?=$(OW_USER)/action-swift-v5.7
+OW_COMPILER?=$(OW_USER)/action-swift-v5.7
+WSK?=wsk -i
+MAIN=async_main
+PACKAGE=test
+SRC=$(MAIN).swift
+NAME=swift-$(MAIN)-single
+ZIP=$(MAIN).zip
+
+deploy: package.done $(ZIP)
+ $(WSK) action update $(PACKAGE)/$(NAME) $(ZIP) --main $(MAIN) --docker
$(OW_RUNTIME)
+
+devel: package.done $(SRC)
+ $(WSK) action update $(PACKAGE)/$(NAME) $(SRC) --main $(MAIN) --docker
$(OW_COMPILER)
+
+$(ZIP): $(SRC)
+ docker run -i $(OW_COMPILER) -compile $(MAIN) <$(SRC) >$(ZIP)
+
+clean:
+ $(WSK) action delete $(PACKAGE)/$(NAME)
+ rm $(ZIP) package.done
+
+test: test.json
+ $(WSK) action invoke test/$(NAME) -r --blocking -d
+ $(WSK) action invoke test/$(NAME) -P test.json -r --blocking -d
+
+test.json:
+ echo '{ "name": "Mike" }' >test.json
+
+package.done:
+ $(WSK) package update $(PACKAGE)
+ touch package.done
+
+.PHONY: deploy devel test clean
diff --git a/examples/swift-async-main-single/main.swift
b/examples/swift-async-main-single/main.swift
new file mode 100644
index 0000000..164cdf6
--- /dev/null
+++ b/examples/swift-async-main-single/main.swift
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Foundation
+import _Concurrency
+
+func main(args: Any) async -> Any {
+ do {
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+ let newArgs = args as! [String:Any]
+ if let name = newArgs["name"] as? String {
+ return [ "greeting" : "Hello \(name)!" ]
+ } else {
+ return [ "greeting" : "Hello stranger!" ]
+ }
+ } catch {
+ return ["error:" : String(describing: error)]
+ }
+}
+
+func mainenv(args: Any) -> Any {
+ let env = ProcessInfo.processInfo.environment
+ var a = "???"
+ var b = "???"
+ var c = "???"
+ var d = "???"
+ var e = "???"
+ var f = "???"
+ if let v : String = env["__OW_API_HOST"] {
+ a = "\(v)"
+ }
+ if let v : String = env["__OW_API_KEY"] {
+ b = "\(v)"
+ }
+ if let v : String = env["__OW_NAMESPACE"] {
+ c = "\(v)"
+ }
+ if let v : String = env["__OW_ACTION_NAME"] {
+ d = "\(v)"
+ }
+ if let v : String = env["__OW_ACTIVATION_ID"] {
+ e = "\(v)"
+ }
+ if let v : String = env["__OW_DEADLINE"] {
+ f = "\(v)"
+ }
+ return ["api_host": a, "api_key": b, "namespace": c, "action_name": d,
"activation_id": e, "deadline": f]
+}
diff --git a/settings.gradle
b/examples/swift-async-main-zip/HelloSwift5/Package.swift
similarity index 58%
copy from settings.gradle
copy to examples/swift-async-main-zip/HelloSwift5/Package.swift
index 8079687..3b3e045 100644
--- a/settings.gradle
+++ b/examples/swift-async-main-zip/HelloSwift5/Package.swift
@@ -1,3 +1,6 @@
+// swift-tools-version:5.7
+// The swift-tools-version declares the minimum version of Swift required to
build this package.
+
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -15,31 +18,20 @@
* limitations under the License.
*/
-include 'tests'
-
-include 'core:swift51Action'
-
-include 'core:swift53Action'
-
-include 'core:swift54Action'
-
-rootProject.name = 'runtime-swift'
-
-gradle.ext.openwhisk = [
- version: '1.0.1-SNAPSHOT'
-]
-
-gradle.ext.scala = [
- version: '2.12.7',
- depVersion : '2.12',
- compileFlags: ['-feature', '-unchecked', '-deprecation',
'-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
- version: '1.5.0',
- config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
-
-gradle.ext.akka = [version : '2.6.12']
-gradle.ext.akka_http = [version : '10.2.4']
-
+import PackageDescription
+
+let package = Package(
+ name: "Action",
+ products: [
+ .executable(
+ name: "Action",
+ targets: ["Action"]
+ )
+ ],
+ targets: [
+ .target(
+ name: "Action",
+ path: "."
+ )
+ ]
+)
diff --git a/settings.gradle
b/examples/swift-async-main-zip/HelloSwift5/Sources/main.swift
similarity index 58%
copy from settings.gradle
copy to examples/swift-async-main-zip/HelloSwift5/Sources/main.swift
index 8079687..5cbbd7e 100644
--- a/settings.gradle
+++ b/examples/swift-async-main-zip/HelloSwift5/Sources/main.swift
@@ -15,31 +15,16 @@
* limitations under the License.
*/
-include 'tests'
-
-include 'core:swift51Action'
-
-include 'core:swift53Action'
-
-include 'core:swift54Action'
-
-rootProject.name = 'runtime-swift'
-
-gradle.ext.openwhisk = [
- version: '1.0.1-SNAPSHOT'
-]
-
-gradle.ext.scala = [
- version: '2.12.7',
- depVersion : '2.12',
- compileFlags: ['-feature', '-unchecked', '-deprecation',
'-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
- version: '1.5.0',
- config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
-
-gradle.ext.akka = [version : '2.6.12']
-gradle.ext.akka_http = [version : '10.2.4']
-
+func main(args: Any) async -> Any {
+ do {
+ try await Task.sleep(nanoseconds: 1_000_000_000)
+ let newArgs = args as! [String:Any]
+ if let name = newArgs["name"] as? String {
+ return [ "greeting" : "Hello \(name)!" ]
+ } else {
+ return [ "greeting" : "Hello stranger!" ]
+ }
+ } catch {
+ return ["error:" : String(describing: error)]
+ }
+}
diff --git a/examples/swift-async-main-zip/Makefile
b/examples/swift-async-main-zip/Makefile
new file mode 100644
index 0000000..6868e8b
--- /dev/null
+++ b/examples/swift-async-main-zip/Makefile
@@ -0,0 +1,56 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+OW_USER?=whisk
+OW_RUNTIME?=$(OW_USER)/action-swift-v5.7
+OW_COMPILER?=$(OW_USER)/action-swift-v5.7
+WSK?=wsk -i
+MAIN=main
+PACKAGE=test
+SRCS=HelloSwift5/Sources/$(MAIN).swift HelloSwift5/Package.swift
+NAME=swift-$(MAIN)-zip
+BINZIP=$(MAIN)-bin.zip
+SRCZIP=$(MAIN)-src.zip
+
+deploy: package.done $(BINZIP)
+ $(WSK) action update $(PACKAGE)/$(NAME) $(BINZIP) --main $(MAIN)
--docker $(OW_RUNTIME)
+
+devel: package.done $(SRCZIP)
+ $(WSK) action update $(PACKAGE)/$(NAME) $(SRCZIP) --main $(MAIN)
--docker $(OW_COMPILER)
+
+$(BINZIP): $(SRCS)
+ cd HelloSwift5 ; zip - -r * | docker run -i $(OW_COMPILER) -compile
$(MAIN) >../$(BINZIP)
+
+$(SRCZIP): $(SRCS)
+ cd HelloSwift5 ; zip ../$(SRCZIP) -r *
+
+clean:
+ -$(WSK) action delete $(PACKAGE)/$(NAME)
+ -rm $(BINZIP) $(SRCZIP) package.done test.json
+
+test: test.json
+ $(WSK) action invoke test/$(NAME) -r
+ $(WSK) action invoke test/$(NAME) -P test.json -r
+
+test.json:
+ echo '{ "name": "Mike" }' >test.json
+
+package.done:
+ $(WSK) package update $(PACKAGE)
+ touch package.done
+
+.PHONY: deploy devel test clean
diff --git a/settings.gradle b/settings.gradle
index 8079687..19a4a3e 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -23,6 +23,8 @@ include 'core:swift53Action'
include 'core:swift54Action'
+include 'core:swift57Action'
+
rootProject.name = 'runtime-swift'
gradle.ext.openwhisk = [
diff --git a/tests/dat/actions/Makefile b/tests/dat/actions/Makefile
index a5d9055..8dba11b 100644
--- a/tests/dat/actions/Makefile
+++ b/tests/dat/actions/Makefile
@@ -16,8 +16,8 @@
#
OW_USER?=whisk
-OW_COMPILER?=$(OW_USER)/action-swift-v5.3
-OUT?=../../build/swift5.3
+OW_COMPILER?=$(OW_USER)/action-swift-v5.7
+OUT?=../../build/swift5.7
define Build
cd $(1); \
docker run -i $(OW_COMPILER) -compile main <./Sources/main.swift
>$(OUT)/$(1).zip
@@ -48,6 +48,9 @@ Swifty5:
SwiftyCodable5:
$(call BuildWithLib,SwiftyRequestCodable5)
+SwiftyCodable57:
+ $(call BuildWithLib,SwiftyRequestCodable57)
+
all: Hello HelloCodable Swifty SwiftyCodable Hello5 HelloCodable5 Swifty5
SwiftyCodable5
.PHONY: Hello HelloCodable Swifty SwiftyCodable Hello5 HelloCodable5 Swifty5
SwiftyCodable5
diff --git a/tests/dat/actions/SwiftyRequest5/Package.resolved
b/tests/dat/actions/SwiftyRequest5/Package.resolved
deleted file mode 100644
index ff2bbd2..0000000
--- a/tests/dat/actions/SwiftyRequest5/Package.resolved
+++ /dev/null
@@ -1,79 +0,0 @@
-{
- "object": {
- "pins": [
- {
- "package": "async-http-client",
- "repositoryURL":
"https://github.com/swift-server/async-http-client.git",
- "state": {
- "branch": null,
- "revision": "51dc885a30ca704b02fa803099b0a9b5b38067b6",
- "version": "1.0.0"
- }
- },
- {
- "package": "CircuitBreaker",
- "repositoryURL": "https://github.com/IBM-Swift/CircuitBreaker.git",
- "state": {
- "branch": null,
- "revision": "e9345aa721ca4da428777f2e953b9ad534017675",
- "version": "5.0.3"
- }
- },
- {
- "package": "LoggerAPI",
- "repositoryURL": "https://github.com/IBM-Swift/LoggerAPI.git",
- "state": {
- "branch": null,
- "revision": "3357dd9526cdf9436fa63bb792b669e6efdc43da",
- "version": "1.9.0"
- }
- },
- {
- "package": "swift-log",
- "repositoryURL": "https://github.com/apple/swift-log.git",
- "state": {
- "branch": null,
- "revision": "74d7b91ceebc85daf387ebb206003f78813f71aa",
- "version": "1.2.0"
- }
- },
- {
- "package": "swift-nio",
- "repositoryURL": "https://github.com/apple/swift-nio.git",
- "state": {
- "branch": null,
- "revision": "bc661cbb771328e09faa65432628e9ff6fd333d5",
- "version": "2.10.0"
- }
- },
- {
- "package": "swift-nio-extras",
- "repositoryURL": "https://github.com/apple/swift-nio-extras.git",
- "state": {
- "branch": null,
- "revision": "ed97628fa310c314c4a5cd8038445054b2991f07",
- "version": "1.3.1"
- }
- },
- {
- "package": "swift-nio-ssl",
- "repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
- "state": {
- "branch": null,
- "revision": "e5c1af45ac934ac0a6117b2927a51d845cf4f705",
- "version": "2.4.3"
- }
- },
- {
- "package": "SwiftyRequest",
- "repositoryURL": "https://github.com/IBM-Swift/SwiftyRequest.git",
- "state": {
- "branch": null,
- "revision": "34cf8de7c3920220392d557c8c84b9f06c8c3978",
- "version": "3.1.0"
- }
- }
- ]
- },
- "version": 1
-}
diff --git a/tests/dat/actions/SwiftyRequestAsyncCodable57/Package.resolved
b/tests/dat/actions/SwiftyRequestAsyncCodable57/Package.resolved
new file mode 100644
index 0000000..888cd6c
--- /dev/null
+++ b/tests/dat/actions/SwiftyRequestAsyncCodable57/Package.resolved
@@ -0,0 +1,86 @@
+{
+ "pins" : [
+ {
+ "identity" : "async-http-client",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/swift-server/async-http-client.git",
+ "state" : {
+ "revision" : "5bee16a79922e3efcb5cea06ecd27e6f8048b56b",
+ "version" : "1.13.1"
+ }
+ },
+ {
+ "identity" : "swift-atomics",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-atomics.git",
+ "state" : {
+ "revision" : "ff3d2212b6b093db7f177d0855adbc4ef9c5f036",
+ "version" : "1.0.3"
+ }
+ },
+ {
+ "identity" : "swift-collections",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-collections.git",
+ "state" : {
+ "revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2",
+ "version" : "1.0.4"
+ }
+ },
+ {
+ "identity" : "swift-log",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-log.git",
+ "state" : {
+ "revision" : "6fe203dc33195667ce1759bf0182975e4653ba1c",
+ "version" : "1.4.4"
+ }
+ },
+ {
+ "identity" : "swift-nio",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio.git",
+ "state" : {
+ "revision" : "7e3b50b38e4e66f31db6cf4a784c6af148bac846",
+ "version" : "2.46.0"
+ }
+ },
+ {
+ "identity" : "swift-nio-extras",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio-extras.git",
+ "state" : {
+ "revision" : "91dd2d61fb772e1311bb5f13b59266b579d77e42",
+ "version" : "1.15.0"
+ }
+ },
+ {
+ "identity" : "swift-nio-http2",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio-http2.git",
+ "state" : {
+ "revision" : "d6656967f33ed8b368b38e4b198631fc7c484a40",
+ "version" : "1.23.1"
+ }
+ },
+ {
+ "identity" : "swift-nio-ssl",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio-ssl.git",
+ "state" : {
+ "revision" : "4fb7ead803e38949eb1d6fabb849206a72c580f3",
+ "version" : "2.23.0"
+ }
+ },
+ {
+ "identity" : "swift-nio-transport-services",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-nio-transport-services.git",
+ "state" : {
+ "revision" : "c0d9a144cfaec8d3d596aadde3039286a266c15c",
+ "version" : "1.15.0"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/settings.gradle
b/tests/dat/actions/SwiftyRequestAsyncCodable57/Package.swift
similarity index 55%
copy from settings.gradle
copy to tests/dat/actions/SwiftyRequestAsyncCodable57/Package.swift
index 8079687..401dadb 100644
--- a/settings.gradle
+++ b/tests/dat/actions/SwiftyRequestAsyncCodable57/Package.swift
@@ -1,3 +1,6 @@
+// swift-tools-version:5.7
+// The swift-tools-version declares the minimum version of Swift required to
build this package.
+
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
@@ -15,31 +18,27 @@
* limitations under the License.
*/
-include 'tests'
-
-include 'core:swift51Action'
-
-include 'core:swift53Action'
-
-include 'core:swift54Action'
-
-rootProject.name = 'runtime-swift'
-
-gradle.ext.openwhisk = [
- version: '1.0.1-SNAPSHOT'
-]
-
-gradle.ext.scala = [
- version: '2.12.7',
- depVersion : '2.12',
- compileFlags: ['-feature', '-unchecked', '-deprecation',
'-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
- version: '1.5.0',
- config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
-
-gradle.ext.akka = [version : '2.6.12']
-gradle.ext.akka_http = [version : '10.2.4']
-
+import PackageDescription
+
+let package = Package(
+ name: "Action",
+ platforms: [
+ .macOS(.v12),
+ ],
+ products: [
+ .executable(
+ name: "Action",
+ targets: ["Action"]
+ )
+ ],
+ dependencies: [
+ .package(url: "https://github.com/swift-server/async-http-client.git",
from: "1.9.0")
+ ],
+ targets: [
+ .executableTarget(
+ name: "Action",
+ dependencies: [.product(name: "AsyncHTTPClient", package:
"async-http-client")],
+ path: "."
+ )
+ ]
+)
diff --git a/tests/dat/actions/SwiftyRequestAsyncCodable57/Sources/main.swift
b/tests/dat/actions/SwiftyRequestAsyncCodable57/Sources/main.swift
new file mode 100644
index 0000000..acfcf1e
--- /dev/null
+++ b/tests/dat/actions/SwiftyRequestAsyncCodable57/Sources/main.swift
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import AsyncHTTPClient
+import Foundation
+import _Concurrency
+import NIOCore
+import NIOFoundationCompat
+
+enum RequestError: Error {
+ case requestError
+}
+struct AnInput: Codable {
+ let url: String?
+}
+
+struct AnOutput: Codable {
+ let args: [String: String]
+ let headers: [String: String]
+ let origin: String
+ let url: String
+}
+
+let httpClient = HTTPClient(eventLoopGroupProvider: .createNew)
+let decoder = JSONDecoder()
+
+func main(param: AnInput) async throws -> AnOutput {
+
+ let echoURL = param.url ?? "https://httpbin.org/get"
+ let request = HTTPClientRequest(url: echoURL)
+ let response = try await httpClient.execute(request, timeout: .seconds(3))
+ if response.status == .ok {
+ let bytes = try await response.body.collect(upTo: 1024 * 1024) // 1 MB
Buffer
+ let data = Data(buffer: bytes)
+ return try decoder.decode(AnOutput.self, from: data)
+ } else {
+ throw RequestError.requestError
+ }
+}
diff --git a/tests/dat/actions/SwiftyRequestCodable5/Package.resolved
b/tests/dat/actions/SwiftyRequestCodable5/Package.resolved
deleted file mode 100644
index ff2bbd2..0000000
--- a/tests/dat/actions/SwiftyRequestCodable5/Package.resolved
+++ /dev/null
@@ -1,79 +0,0 @@
-{
- "object": {
- "pins": [
- {
- "package": "async-http-client",
- "repositoryURL":
"https://github.com/swift-server/async-http-client.git",
- "state": {
- "branch": null,
- "revision": "51dc885a30ca704b02fa803099b0a9b5b38067b6",
- "version": "1.0.0"
- }
- },
- {
- "package": "CircuitBreaker",
- "repositoryURL": "https://github.com/IBM-Swift/CircuitBreaker.git",
- "state": {
- "branch": null,
- "revision": "e9345aa721ca4da428777f2e953b9ad534017675",
- "version": "5.0.3"
- }
- },
- {
- "package": "LoggerAPI",
- "repositoryURL": "https://github.com/IBM-Swift/LoggerAPI.git",
- "state": {
- "branch": null,
- "revision": "3357dd9526cdf9436fa63bb792b669e6efdc43da",
- "version": "1.9.0"
- }
- },
- {
- "package": "swift-log",
- "repositoryURL": "https://github.com/apple/swift-log.git",
- "state": {
- "branch": null,
- "revision": "74d7b91ceebc85daf387ebb206003f78813f71aa",
- "version": "1.2.0"
- }
- },
- {
- "package": "swift-nio",
- "repositoryURL": "https://github.com/apple/swift-nio.git",
- "state": {
- "branch": null,
- "revision": "bc661cbb771328e09faa65432628e9ff6fd333d5",
- "version": "2.10.0"
- }
- },
- {
- "package": "swift-nio-extras",
- "repositoryURL": "https://github.com/apple/swift-nio-extras.git",
- "state": {
- "branch": null,
- "revision": "ed97628fa310c314c4a5cd8038445054b2991f07",
- "version": "1.3.1"
- }
- },
- {
- "package": "swift-nio-ssl",
- "repositoryURL": "https://github.com/apple/swift-nio-ssl.git",
- "state": {
- "branch": null,
- "revision": "e5c1af45ac934ac0a6117b2927a51d845cf4f705",
- "version": "2.4.3"
- }
- },
- {
- "package": "SwiftyRequest",
- "repositoryURL": "https://github.com/IBM-Swift/SwiftyRequest.git",
- "state": {
- "branch": null,
- "revision": "34cf8de7c3920220392d557c8c84b9f06c8c3978",
- "version": "3.1.0"
- }
- }
- ]
- },
- "version": 1
-}
diff --git a/tests/dat/build.sh b/tests/dat/build.sh
index f9c2751..aace1b2 100755
--- a/tests/dat/build.sh
+++ b/tests/dat/build.sh
@@ -32,3 +32,9 @@ set -e
../../tools/build/compile5.sh action-swift-v5.4 HelloSwift5Codable swift5.4
"-v"
../../tools/build/compile5.sh action-swift-v5.4 SwiftyRequest5 swift5.4 "-v"
../../tools/build/compile5.sh action-swift-v5.4 SwiftyRequestCodable5
swift5.4 "-v"
+
+../../tools/build/compile5.sh action-swift-v5.7 HelloSwift5 swift5.7 "-v"
+../../tools/build/compile5.sh action-swift-v5.7 HelloSwift5Codable swift5.7
"-v"
+../../tools/build/compile5.sh action-swift-v5.7 SwiftyRequest5 swift5.7 "-v"
+../../tools/build/compile5.sh action-swift-v5.7 SwiftyRequestCodable5
swift5.7 "-v"
+../../tools/build/compile5.sh action-swift-v5.7 SwiftyRequestAsyncCodable57
swift5.7 "-v"
diff --git
a/tests/src/test/scala/runtime/actionContainers/Swift57ActionContainerTests.scala
b/tests/src/test/scala/runtime/actionContainers/Swift57ActionContainerTests.scala
new file mode 100644
index 0000000..e1c15a8
--- /dev/null
+++
b/tests/src/test/scala/runtime/actionContainers/Swift57ActionContainerTests.scala
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package runtime.actionContainers
+
+import java.io.File
+
+import actionContainers.ResourceHelpers
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+import spray.json.{JsObject, JsString}
+
+@RunWith(classOf[JUnitRunner])
+class Swift57ActionContainerTests extends SwiftActionContainerTests {
+ override lazy val swiftContainerImageName = "action-swift-v5.7"
+ override lazy val swiftBinaryName =
"tests/dat/build/swift5.7/HelloSwift5.zip"
+ lazy val partyCompile = "tests/dat/build/swift5.7/SwiftyRequest5.zip"
+ lazy val partyCompileCodable =
"tests/dat/build/swift5.7/SwiftyRequestCodable5.zip"
+ lazy val partyCompileAsyncCodable =
"tests/dat/build/swift5.7/SwiftyRequestAsyncCodable57.zip"
+
+ val httpCode = """
+ | import Foundation
+ | import Dispatch
+ | #if canImport(FoundationNetworking)
+ | import FoundationNetworking
+ | #endif
+ | func main(args: Any) -> Any {
+ | var resp :[String:Any] = ["error":"getUrl failed"]
+ | let newArgs = args as! [String:Any]
+ | guard let urlStr = newArgs["getUrl"] as? String else {
+ | return ["error":"getUrl not found in action
input"]
+ | }
+ | guard let url = URL(string: urlStr) else {
+ | return ["error":"invalid url string \(urlStr)"]
+ | }
+ | let request = URLRequest(url: url)
+ | let session = URLSession(configuration: .default)
+ | let semaphore = DispatchSemaphore(value: 0)
+ | let task = session.dataTask(with: request,
completionHandler: {data, response, error -> Void in
+ | print("done with http request")
+ | if let error = error {
+ | print("There was an error \(error)")
+ | } else if let data = data,
+ | let response = response as? HTTPURLResponse,
+ | response.statusCode == 200 {
+ | do {
+ | let respJson = try
JSONSerialization.jsonObject(with: data)
+ | if respJson is [String:Any] {
+ | resp = respJson as! [String:Any]
+ | } else {
+ | resp = ["error":"Response from server
is not a dictionary"]
+ | }
+ | } catch {
+ | resp = ["error":"Error creating json from
response: \(error)"]
+ | }
+ | }
+ | semaphore.signal()
+ | })
+ | task.resume()
+ | _ = semaphore.wait(timeout: .distantFuture)
+ | return resp
+ | }
+ """.stripMargin
+
+ it should "support ability to use 3rd party packages like SwiftyRequest" in {
+ val zip = new File(partyCompile).toPath
+ val code = ResourceHelpers.readAsBase64(zip)
+
+ val (out, err) = withActionContainer() { c =>
+ val (initCode, initRes) = c.init(initPayload(code))
+ initCode should be(200)
+
+ val args = JsObject("message" -> (JsString("serverless")))
+ val (runCode, runRes) = c.run(runPayload(args))
+
+ runCode should be(200)
+ val json = runRes.get.fields.get("json")
+ json shouldBe Some(args)
+ }
+
+ checkStreams(out, err, {
+ case (o, e) =>
+ if (enforceEmptyOutputStream) o shouldBe empty
+ e shouldBe empty
+ })
+ }
+
+ it should "support ability to use escaping completion in Codable" in {
+ val zip = new File(partyCompileCodable).toPath
+ val code = ResourceHelpers.readAsBase64(zip)
+
+ val (out, err) = withActionContainer() { c =>
+ val (initCode, initRes) = c.init(initPayload(code, main = "mainCodable"))
+ initCode should be(200)
+
+ val (runCode, runRes) = c.run(runPayload(JsObject()))
+
+ runCode should be(200)
+ runRes.get.fields.get("greeting") shouldBe Some(JsString("success"))
+
+ }
+
+ checkStreams(out, err, {
+ case (o, e) =>
+ if (enforceEmptyOutputStream) o shouldBe empty
+ e shouldBe empty
+ })
+ }
+
+ it should "support ability to use async with Codable" in {
+ val zip = new File(partyCompileAsyncCodable).toPath
+ val code = ResourceHelpers.readAsBase64(zip)
+
+ val (out, err) = withActionContainer() { c =>
+ val (initCode, initRes) = c.init(initPayload(code, main = "mainCodable"))
+ initCode should be(200)
+
+ val (runCode, runRes) = c.run(runPayload(JsObject()))
+
+ runCode should be(200)
+ runRes.get.fields.get("url") shouldBe
Some(JsString("https://httpbin.org/get"))
+
+ }
+
+ checkStreams(out, err, {
+ case (o, e) =>
+ if (enforceEmptyOutputStream) o shouldBe empty
+ e shouldBe empty
+ })
+ }
+}
diff --git a/settings.gradle
b/tests/src/test/scala/runtime/actionContainers/Swift57CodableActionContainerTests.scala
similarity index 58%
copy from settings.gradle
copy to
tests/src/test/scala/runtime/actionContainers/Swift57CodableActionContainerTests.scala
index 8079687..431bdc9 100644
--- a/settings.gradle
+++
b/tests/src/test/scala/runtime/actionContainers/Swift57CodableActionContainerTests.scala
@@ -15,31 +15,13 @@
* limitations under the License.
*/
-include 'tests'
+package runtime.actionContainers
-include 'core:swift51Action'
-
-include 'core:swift53Action'
-
-include 'core:swift54Action'
-
-rootProject.name = 'runtime-swift'
-
-gradle.ext.openwhisk = [
- version: '1.0.1-SNAPSHOT'
-]
-
-gradle.ext.scala = [
- version: '2.12.7',
- depVersion : '2.12',
- compileFlags: ['-feature', '-unchecked', '-deprecation',
'-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
- version: '1.5.0',
- config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
-
-gradle.ext.akka = [version : '2.6.12']
-gradle.ext.akka_http = [version : '10.2.4']
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+@RunWith(classOf[JUnitRunner])
+class Swift57CodableActionContainerTests extends
SwiftCodableActionContainerTests {
+ override lazy val swiftContainerImageName = "action-swift-v5.7"
+ override lazy val swiftBinaryName =
"tests/dat/build/swift5.7/HelloSwift5Codable.zip"
+}
diff --git a/settings.gradle
b/tests/src/test/scala/runtime/sdk/Swift57SDKTests.scala
similarity index 58%
copy from settings.gradle
copy to tests/src/test/scala/runtime/sdk/Swift57SDKTests.scala
index 8079687..d494d33 100644
--- a/settings.gradle
+++ b/tests/src/test/scala/runtime/sdk/Swift57SDKTests.scala
@@ -15,31 +15,12 @@
* limitations under the License.
*/
-include 'tests'
+package runtime.sdk
-include 'core:swift51Action'
-
-include 'core:swift53Action'
-
-include 'core:swift54Action'
-
-rootProject.name = 'runtime-swift'
-
-gradle.ext.openwhisk = [
- version: '1.0.1-SNAPSHOT'
-]
-
-gradle.ext.scala = [
- version: '2.12.7',
- depVersion : '2.12',
- compileFlags: ['-feature', '-unchecked', '-deprecation',
'-Xfatal-warnings', '-Ywarn-unused-import']
-]
-
-gradle.ext.scalafmt = [
- version: '1.5.0',
- config: new File(rootProject.projectDir, '.scalafmt.conf')
-]
-
-gradle.ext.akka = [version : '2.6.12']
-gradle.ext.akka_http = [version : '10.2.4']
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+@RunWith(classOf[JUnitRunner])
+class Swift57SDKTests extends SwiftSDKTests {
+ override lazy val actionKind = "swift:5.7"
+}
diff --git a/tools/travis/build.sh b/tools/travis/build.sh
index bf6e83b..3239f4a 100755
--- a/tools/travis/build.sh
+++ b/tools/travis/build.sh
@@ -49,6 +49,7 @@ TERM=dumb ./gradlew \
:core:swift51Action:distDocker \
:core:swift53Action:distDocker \
:core:swift54Action:distDocker \
+:core:swift57Action:distDocker \
-PdockerImagePrefix=${IMAGE_PREFIX}
# Compile test files
diff --git a/tools/travis/publish.sh b/tools/travis/publish.sh
index d2eebdd..cd1694e 100755
--- a/tools/travis/publish.sh
+++ b/tools/travis/publish.sh
@@ -36,6 +36,8 @@ elif [ ${RUNTIME_VERSION} == "5.3" ]; then
RUNTIME="swift53Action"
elif [ ${RUNTIME_VERSION} == "5.4" ]; then
RUNTIME="swift54Action"
+elif [ ${RUNTIME_VERSION} == "5.7" ]; then
+ RUNTIME="swift57Action"
fi
if [[ ! -z ${DOCKER_USER} ]] && [[ ! -z ${DOCKER_PASSWORD} ]]; then