Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 28 additions & 10 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ var testTargets: [Target] = [
var dependencies: [PackageDescription.Package.Dependency] = [
// because SPM cant resolve it by their own ((((:
// .package(url: "https://github.com/kylef/PathKit.git", from: "0.9.0"),
.package(url: "https://github.com/LastSprint/SwagGen", .revision("285ee89b027344520cbda903ea1c74bf0f9baac8")),
.package(url: "https://github.com/stencilproject/Stencil", from: "0.14.1"),
.package(url: "https://github.com/surfstudio/surf-SwagGen", .revision("c10461f6eb74d8c834e7621e82068a5d938be27d")),
.package(url: "https://github.com/stencilproject/Stencil", from: "0.15.1"),
.package(url: "https://github.com/jakeheis/SwiftCLI", from: "6.0.3"),
.package(url: "https://github.com/onevcat/Rainbow", from: "3.1.5"),
.package(url: "https://github.com/jpsim/Yams", from: "1.0.0"),
Expand Down
5 changes: 4 additions & 1 deletion Sources/CodeGenerator/Models/PropertyModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,16 @@ public struct PropertyModel {
public let description: String?
public let type: PossibleType
public let isNullable: Bool
public let pattern: String?

/// This value will be used as type for generation
public let typeModel: ItemTypeModel

init(name: String,
description: String?,
type: PropertyModel.PossibleType,
isNullable: Bool) {
isNullable: Bool,
pattern: String?) {
self.name = name
self.description = description
self.type = type
Expand All @@ -96,6 +98,7 @@ public struct PropertyModel {
isObject: type.isObject,
enumTypeName: type.enumTypeName,
aliasTypeName: type.aliasTypeName)
self.pattern = pattern
}
}

Expand Down
8 changes: 7 additions & 1 deletion Sources/CodeGenerator/Stages/GenerationStage/Template.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,23 @@ public struct Template: Decodable {
/// Example: Path/To/Project/Models/{name}
public let destinationPath: String

/// Environment where custom variables are stored
/// These variables can be used in a Stencil template.
public let environment: [String: String]?

public init(type: Template.TemplateType,
nameSuffix: String?,
fileExtension: String,
fileNameCase: FileNameCase? = .camelCase,
templatePath: String,
destinationPath: String) {
destinationPath: String,
environment: [String: String]?) {
self.type = type
self.nameSuffix = nameSuffix
self.fileExtension = fileExtension
self.fileNameCase = fileNameCase
self.templatePath = templatePath
self.destinationPath = destinationPath
self.environment = environment
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ public class DefaultTemplateFiller: TemplateFiller {
templateExtension.registerStringFilter("sanitizeUrlPath") {
$0.sanitizeUrlPath()
}


templateExtension.registerStringFilter("withEscapedCharacters") {
$0.withEscapedCharacters()
}

return templateExtension
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,14 @@ public class Resolver {
return .init(name: property.name,
description: property.description,
type: .array(.init(name: "", itemsType: try arrayItemTypeUnwrapper(arr.itemsType))),
isNullable: property.nullable)
isNullable: property.nullable,
pattern: property.pattern)
case .simple(let val):
return .init(name: property.name,
description: property.description,
type: try propertyTypeUnwrapper(val),
isNullable: property.nullable)
isNullable: property.nullable,
pattern: property.pattern)
}
}

Expand Down
11 changes: 9 additions & 2 deletions Sources/Common/Extensions/String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ extension String {
public func replaceNameTemplate(with name: String) -> String {
return self.replacingOccurrences(of: "\\{.*?\\}", with: name, options: .regularExpression)
}

/// Return package name from a full api file name.
/// For example:
/// self = "/users/username/swagger/products/api.yaml
Expand All @@ -136,7 +136,14 @@ extension String {
public func sanitizeUrlPath() -> String {
return String(self.drop { $0 == "/" })
}


/// String with all escaped characters.
public func withEscapedCharacters() -> String {
return self.unicodeScalars
.map { $0.escaped(asASCII: true) }
.joined()
}

private func pathToCamelCase() -> String {
return self
.split(whereSeparator: { $0 == "/" || $0 == "_" })
Expand Down
11 changes: 10 additions & 1 deletion Sources/GASTBuilder/Builders/AnySchemaBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,21 @@ public struct AnySchemaBuilder: SchemaBuilder {
isNullable = property.schema.metadata.nullable
}

var pattern: String?

switch property.schema.type {
case .string(let stringSchema):
pattern = stringSchema.pattern
default:
break
}

return PropertyNode(name: property.name,
type: type,
description: property.schema.metadata.description,
example: property.schema.metadata.example,
nullable: isNullable)
nullable: isNullable,
pattern: pattern)
}

return SchemaModelNode(name: name, properties: properties, description: meta.description, apiDefinitionFileRef: apiDefinitionFileRef)
Expand Down
5 changes: 4 additions & 1 deletion Sources/GASTTree/PropertyNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@ public struct PropertyNode {
public let description: String?
public let example: Any?
public let nullable: Bool
public let pattern: String?

public init(name: String,
type: PossibleType,
description: String?,
example: Any?,
nullable: Bool) {
nullable: Bool,
pattern: String?) {
self.name = name
self.type = type
self.description = description
self.example = example
self.nullable = nullable
self.pattern = pattern
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public struct BuildCodeGeneratorPipelineFactory {
}

public static func build(templates: [Template],
globalEnvironment: [String: String],
specificationRootPath: String,
astNodesToExclude: Set<String>,
serviceName: String,
Expand Down Expand Up @@ -81,6 +82,7 @@ public struct BuildCodeGeneratorPipelineFactory {
specificationRootPath: specificationRootPath,
templateFiller: templateFiller,
modelExtractor: modelExtractor,
globalEnvironment: globalEnvironment,
prefixCutter: prefixCutter
).erase()
).erase(),
Expand Down
1 change: 1 addition & 0 deletions Sources/Pipelines/CLI/Generation/GenerationCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public class GenerationCommand: Command {

let pipeline = BuildCodeGeneratorPipelineFactory.build(
templates: config.templates,
globalEnvironment: config.environment ?? [:],
specificationRootPath: config.specificationRootPath ?? "",
astNodesToExclude: try Utils.Urls.makeAstNodeRefsAbsolute(refs: rawAstNodesToExclude),
serviceName: serviceName,
Expand Down
1 change: 1 addition & 0 deletions Sources/Pipelines/CLI/Generation/GenerationConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public struct AnalytcsConfig: Decodable {
}

public struct GenerationConfig: Decodable {
public var environment: [String: String]?
public var templates: [Template]
public var analytcsConfig: AnalytcsConfig?
public var prefixesToCutDownInServiceNames: [String]?
Expand Down
32 changes: 29 additions & 3 deletions Sources/Pipelines/CodeGen/ServiceGenerationStage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public struct ServiceGenerationStage: PipelineStage {

var next: AnyPipelineStage<[SourceCode]>

private let globalEnvironment: [String: String]
private let templates: [Template]
private let serviceName: String
private let prefixCutter: PrefixCutter?
Expand All @@ -35,6 +36,7 @@ public struct ServiceGenerationStage: PipelineStage {
specificationRootPath: String,
templateFiller: TemplateFiller,
modelExtractor: ModelExtractor,
globalEnvironment: [String: String],
prefixCutter: PrefixCutter? = nil
) {
self.next = next
Expand All @@ -44,6 +46,7 @@ public struct ServiceGenerationStage: PipelineStage {
self.templateFiller = templateFiller
self.modelExtractor = modelExtractor
self.prefixCutter = prefixCutter
self.globalEnvironment = globalEnvironment
}

public func run(with input: [[PathModel]]) throws {
Expand Down Expand Up @@ -84,45 +87,68 @@ public struct ServiceGenerationStage: PipelineStage {
templates.filter { $0.type == .service },
with: [ContextKeys.service: serviceGenerationModel],
name: serviceGenerationModel.name,
serviceName: serviceName,
apiDefinitionFileRef: serviceGenerationModel.apiDefinitionFileRef)

let generatedModels = try objectModels.flatMap {
return try fillTemplates(templates.filter { $0.type == .model },
with: [ContextKeys.model: $0],
name: $0.name,
serviceName: serviceName,
apiDefinitionFileRef: $0.apiDefinitionFileRef)
}

let generatedEnums = try enumModels.flatMap {
return try fillTemplates(templates.filter { $0.type == .enum },
with: [ContextKeys.enum: $0],
name: $0.name,
serviceName: serviceName,
apiDefinitionFileRef: $0.apiDefinitionFileRef)
}

let generatedTypeAliases = try typeAliasModels.flatMap {
return try fillTemplates(templates.filter { $0.type == .typealias },
with: [ContextKeys.model: $0],
name: $0.name,
serviceName: serviceName,
apiDefinitionFileRef: $0.apiDefinitionFileRef)
}

try next.run(with: generatedService + generatedModels + generatedEnums + generatedTypeAliases)

}

private func fillTemplates(_ templates: [Template], with model: [String: Any], name: String, apiDefinitionFileRef: String) throws -> [SourceCode] {
private func fillTemplates(
_ templates: [Template],
with model: [String: Any],
name: String,
serviceName: String,
apiDefinitionFileRef: String
) throws -> [SourceCode] {
return try templates.map { template in
let context = makeContext(from: model, with: serviceName, in: template)
let sourceCode = try wrap(
templateFiller.fillTemplate(at: template.templatePath, with: model),
message: "While filling template at \(template.templatePath) with model \(name)"
templateFiller.fillTemplate(at: template.templatePath, with: context),
message: "While filling template at \(template.templatePath) with model \(context)"
)
return SourceCode(code: sourceCode,
fileName: template.buildFileName(for: name),
destinationPath: template.buildDestinationPath(for: name),
apiDefinitionFileRef: apiDefinitionFileRef)
}
}

private func makeContext(
from model: [String: Any],
with serviceName: String,
in template: Template
) -> [String: Any] {
let templateEnvironment = template.environment ?? [:]
let environment = globalEnvironment.merging(templateEnvironment, uniquingKeysWith: { _, value in value })
return model
.merging(environment, uniquingKeysWith: { value, _ in value })
.merging(["serviceName": serviceName], uniquingKeysWith: { value, _ in value })
}
}

private extension Template {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public struct StubBuildCodeGeneratorPipelineFactory {
}

public static func build(templates: [Template],
globalEnvironment: [String: String],
specificationRootPath: String,
astNodesToExclude: Set<String>,
serviceName: String,
Expand Down Expand Up @@ -132,6 +133,7 @@ public struct StubBuildCodeGeneratorPipelineFactory {
specificationRootPath: specificationRootPath,
templateFiller: templateFiller,
modelExtractor: modelExtractor,
globalEnvironment: globalEnvironment,
prefixCutter: prefixCutter
).erase()
).erase(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class DataGenerationTest: XCTestCase {

try StubBuildCodeGeneratorPipelineFactory.build(
templates: TestTemplates.swiftTemplateModels,
globalEnvironment: [:],
specificationRootPath: homePath,
astNodesToExclude: [],
serviceName: "PackageSeparation",
Expand Down
Loading