Skip to content

Commit

Permalink
Artifact Submission
Browse files Browse the repository at this point in the history
  • Loading branch information
valentinbootz committed Jun 30, 2022
1 parent 1138a9e commit 7de65b4
Show file tree
Hide file tree
Showing 19 changed files with 482 additions and 119 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ jobs:
name: Build and Test
uses: Apodini/.github/.github/workflows/build-and-test.yml@v1
with:
packagename: ApodiniTemplate
packagename: ApodiniDocumentExport
2 changes: 1 addition & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
name: Build and Test
uses: Apodini/.github/.github/workflows/build-and-test.yml@v1
with:
packagename: ApodiniTemplate
packagename: ApodiniDocumentExport
reuse_action:
name: REUSE Compliance Check
uses: Apodini/.github/.github/workflows/reuse.yml@v1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ jobs:
name: Generate Docs
uses: Apodini/.github/.github/workflows/docs.yml@v1
with:
packagename: ApodiniTemplate
packagename: ApodiniDocumentExport
49 changes: 30 additions & 19 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
// swift-tools-version:5.5

//
// This source file is part of the Apodini open source project
//
// SPDX-FileCopyrightText: 2021 Paul Schmiedmayer and the project authors (see CONTRIBUTORS.md) <[email protected]>
//
// SPDX-License-Identifier: MIT
//
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription


let package = Package(
name: "ApodiniTemplate",
platforms: [
.macOS(.v11)
],
name: "ApodiniDocumentExport",
products: [
.library(name: "ApodiniTemplate", targets: ["ApodiniTemplate"])
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "ApodiniDocumentExport",
targets: ["ApodiniDocumentExport"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser", .upToNextMinor(from: "0.4.4")),
.package(url: "https://github.com/kylef/PathKit.git", from: "1.0.1"),
.package(url: "https://github.com/omochi/FineJSON.git", from: "1.14.0"),
.package(url: "https://github.com/jpsim/Yams.git", from: "4.0.0")
],
targets: [
.target(name: "ApodiniTemplate"),
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "ApodiniDocumentExport",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "PathKit", package: "PathKit"),
.product(name: "FineJSON", package: "FineJSON"),
.product(name: "Yams", package: "Yams")
]),
.testTarget(
name: "ApodiniTemplateTests",
name: "ApodiniDocumentExportTests",
dependencies: [
.target(name: "ApodiniTemplate")
]
)
"ApodiniDocumentExport"
],
resources: [
.process("document.json")
]),
]
)
178 changes: 150 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,158 @@ SPDX-License-Identifier: MIT
-->

## How to use this repository
### Template

When creating a new repository, make sure to select this repository as a repository template.

### Customize the repository

Enter your repository-specific configuration
- Replace the "Package.swift", "Sources" and "Tests" folder with your Swift Package
- Enter the correct Swift Package name (currently "ApodiniTemplate") in the build.yml, pull_request.yml and release.yml files.
- Update the DocC documentation to reflect the name of the new Swift package and adapt the docs and build and test GitHub Actions where the documentation is generated to the updated names to be sure the DocC generation works as expected
- Update the README with your information and replace the links to the license with the new repository.
- Update the status badges to point to the GitHub actions of your repository.
- If you create a new repository in the Apodini organization, you do not need to add a personal access token named "ACCESS_TOKEN". If you create the repo outside the Apodini organization, you need to create such a token with write access to the repo for all GitHub Actions to work. You will need to give the `ApodiniBot` user write access to the repository.

### ⬆️ Remove everything up to here ⬆️

# Project Name

[![Build](https://github.com/Apodini/Template-Repository/actions/workflows/build.yml/badge.svg)](https://github.com/Apodini/Template-Repository/actions/workflows/build.yml)
[![codecov](https://codecov.io/gh/Apodini/Template-Repository/branch/develop/graph/badge.svg?token=5MMKMPO5NR)](https://codecov.io/gh/Apodini/Template-Repository)

## Requirements

## Installation/Setup/Integration

## Usage
# Apodini Document Export

[![Build](https://github.com/Apodini/ApodiniDocumentExport/actions/workflows/build.yml/badge.svg)](https://github.com/Apodini/ApodiniDocumentExport/actions/workflows/build.yml)
[![codecov](https://codecov.io/gh/Apodini/ApodiniDocumentExport/branch/develop/graph/badge.svg?token=5MMKMPO5NR)](https://codecov.io/gh/Apodini/ApodiniDocumentExport)

Create a document to store knowledge on your Apodini web service and export it in a local directory or expose a new endpoint.

> Tip: See `ApodiniSustainability` and `ApodiniMigration` as references to start with your implementation of this use case.
## Getting Started

### Dependency

Add `ApodiniDocumentExport` product to your target dependencies in `package.swift`:
```swift
.product(name: "ApodiniDocumentExport", package: "Apodini")
```

### Documents

The structure of your document is unique to your use case. You may use the `Value` protocol to require conformance to `Codable` and `Hashable`.

```swift
/// A document that describes an Apodini Web Service
public struct Document: Value {
<#code#>
}
```

### Export Options

`ExportOptions` provides a protocol to specify the document's `format` and optional `directory` and `endpoint` properties. `ApodiniDocumentExport` supports `.json` and `.yaml` format. You may use `ArgumentParser` to enable command line arguments.

```swift
struct DocumentExportOptions: ExportOptions {
/// A path to a local directory used to export document
@Option(name: .customLong("directory"), help: "A path to a local directory to export document")
public var directory: String?
/// An endpoint path of the web service used to expose document
@Option(name: .customLong("endpoint"), help: "A path to an endpoint of the web service to expose document")
public var endpoint: String?
/// Format of the document export
///
/// Supports `json` or `yaml` format.
/// - Note: Defaults to `json`
@Option(name: .customLong("format"), help: "Format of the document, either `json` or `yaml`")
public var format: FileFormat = .json

/// Creates an instance of this parsable type using the definitions given by each property’s wrapper.
public init() {}
}
```

### Interface Exporter

Apodini enables you to build a new ``InterfaceExporter`` to collect information on your web service and initialize a `document` instance. This implementation of ``InterfaceExporter/finishedExporting(_:)`` shows how to use `ApodiniDocumentExport` to write a document to a local directory or expose a new endpoint.

```swift
final class DocumentInterfaceExporter: InterfaceExporter {

private let app: Application
private let configuration: DocumentConfiguration
private let logger = Logger(label: <#String#>)

init(_ app: Application, configuration: DocumentConfiguration) {
self.app = app
self.configuration = configuration
}

func export<H>(_ endpoint: Apodini.Endpoint<H>) -> () where H : Handler {
<#code#>
}

func export<H>(blob endpoint: Apodini.Endpoint<H>) -> () where H : Handler, H.Response.Content == Blob {
<#code#>
}

func finishedExporting(_ webService: WebServiceModel) {

app.storage.set(DocumentStorageKey.self, to: document)

guard let options = configuration.exportOptions else {
return logger.notice("No configuration provided to handle document")
}

if let directory = options.directory {
do {
let filePath = try document.write(at: directory, outputFormat: options.format)
logger.info("Document exported at \(filePath) in \(options.format.rawValue)")
} catch {
logger.error("Document export at \(directory) failed with error: \(error)")
}
}

if let endpoint = options.endpoint {
app.httpServer.registerRoute(.GET, endpoint.httpPathComponents) { _ -> String in
options.format.string(of: document)
}
logger.info("Document served at \(endpoint) in \(options.format.rawValue) format")
}
}
}
```

### Document Configuration

Create a ``Apodini/Configuration`` and use ``Application/registerExporter(exporter:)`` to register your ``InterfaceExporter`` implementation with the ``Application``.

```swift
public class DocumentConfiguration: Configuration {

let exportOptions: DocumentExportOptions?

/// Initializer for a ``DocumentConfiguration`` instance
/// - Parameter exportOptions: Export options of the document
public init(_ exportOptions: DocumentExportOptions? = nil) {
self.exportOptions = exportOptions
}

/// Configures `app` by registering the ``InterfaceExporter`` that handles document export
/// - Parameter app: Application instance to register the configuration in Apodini
public func configure(_ app: Application) {
app.registerExporter(exporter: DocumentInterfaceExporter(app, configuration: self))
}
}

public extension WebService {
/// A typealias for ``DocumentConfiguration``
typealias Document = DocumentConfiguration
}
```

This example shows how to use the implementation in your Apodini web service `configuration`.

```swift
struct HelloWorld: WebService {

@OptionGroup
var options: DocumentExportOptions

var configuration: Configuration {
Document(options)
}

var content: some Component {
Greeter()
}
}
```

## 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 [Licenses](https://github.com/Apodini/Template-Repository/tree/develop/LICENSES) for more information.
This project is licensed under the MIT License. See [Licenses](https://github.com/Apodini/ApodiniDocumentExport/tree/develop/LICENSES) for more information.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# ``ApodiniDocumentExport``
54 changes: 54 additions & 0 deletions Sources/ApodiniDocumentExport/ExportOptions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Foundation
import ArgumentParser

/// A typealias for ``OutputFormat``
public typealias FileFormat = OutputFormat

extension OutputFormat: ExpressibleByArgument {}

// MARK: - ExportOptions
/// A protocol that defines export options for `ApodiniMigrator` items
public protocol ExportOptions: ParsableArguments {
/// Optional directory path to export an item
var directory: String? { get set }
/// Optional endpoint path to expose an item
var endpoint: String? { get set }
/// Format of the item to be exported / exposed, either `json` or `yaml`
var format: FileFormat { get set }
}

extension ExportOptions {
init(directory: String? = nil, endpoint: String? = nil, format: FileFormat) {
self.init()
self.directory = directory
self.endpoint = endpoint
self.format = format
}
}

public extension ExportOptions {
/// A convenient static function for initializing an ``ExportOptions`` instance
/// - Parameters:
/// - path: A path to a local directory used to export an item
/// - format: Format of the item to be exported, either `json` or `yaml`. Defaults to `.json`
static func directory(_ path: String, format: FileFormat = .json) -> Self {
.init(directory: path, format: format)
}

/// A convenient static function for initializing an ``ExportOptions`` instance
/// - Parameters:
/// - path: An endpoint path of the web service used to expose an item
/// - format: Format of the item to be exposed, either `json` or `yaml`. Defaults to `.json`
static func endpoint(_ path: String, format: FileFormat = .json) -> Self {
.init(endpoint: path, format: format)
}

/// A convenient static function for initializing an ``ExportOptions`` instance
/// - Parameters:
/// - directory: A path to a local directory used to export an item
/// - endpoint: An endpoint path of the web service used to expose an item
/// - format: Format of the item to be exposed, either `json` or `yaml`. Defaults to `.json`
static func paths(directory: String, endpoint: String, format: FileFormat = .json) -> Self {
.init(directory: directory, endpoint: endpoint, format: format)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Foundation
import PathKit
@_implementationOnly import Yams

public extension Decodable {
/// Initializes self from data
static func decode(from data: Data) throws -> Self {
try JSONDecoder().decode(Self.self, from: data)
}

/// Initializes self from string
static func decode(from string: String) throws -> Self {
try decode(from: string.data())
}

/// Initializes self from the content of path
static func decode(from path: Path) throws -> Self {
guard path.is(.json) || path.is(.yaml) else {
throw DecodingError.dataCorrupted(
.init(
codingPath: [],
debugDescription: "`ApodiniMigrator` only supports decoding of files in either json or yaml format"
)
)
}
let data = try path.read() as Data
if path.is(.yaml) {
return try YAMLDecoder().decode(from: data)
}
return try decode(from: data)
}
}
Loading

0 comments on commit 7de65b4

Please sign in to comment.