From 0920cb7c8409f06ce36248a2e0dad385c588f517 Mon Sep 17 00:00:00 2001 From: Simon Pilkington Date: Wed, 3 Aug 2022 11:36:10 +1000 Subject: [PATCH 1/4] Add the ability in the plugins to use a model defined in a separate package. --- .../SmokeFrameworkGenerateClient/plugin.swift | 86 +++++++++++++++++- .../SmokeFrameworkGenerateHttp1/plugin.swift | 87 ++++++++++++++++++- .../SmokeFrameworkGenerateModel/plugin.swift | 87 ++++++++++++++++++- README.md | 73 ++++++++++++++++ 4 files changed, 322 insertions(+), 11 deletions(-) diff --git a/Plugins/SmokeFrameworkGenerateClient/plugin.swift b/Plugins/SmokeFrameworkGenerateClient/plugin.swift index 815113c..5420bb1 100644 --- a/Plugins/SmokeFrameworkGenerateClient/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateClient/plugin.swift @@ -3,8 +3,21 @@ import Foundation private let targetSuffix = "Client" +enum PluginError: Error { + case unknownModelPackageDependency(packageName: String) + case unknownModelTargetDependency(packageName: String, targetName: String) + case sourceModuleTargetRequired(packageName: String, targetName: String, type: Target.Type) + case unknownModelFilePath(packageName: String, targetName: String, fileName: String) +} + @main struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { + struct SmokeFrameworkCodeGen: Codable { + let modelProductDependency: String? + let modelTargetDependency: String? + let modelFilePath: String + } + /// This plugin's implementation returns a single build command which /// calls `SmokeFrameworkApplicationGenerate` to generate the service client. func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { @@ -16,6 +29,11 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { if baseName.hasSuffix(targetSuffix) { baseName = String(baseName.dropLast(targetSuffix.count)) } + + let inputFile = context.package.directory.appending("smoke-framework-codegen.json") + + let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string) + let clientDirectory = sourcesDirectory.appending("\(baseName)\(targetSuffix)") let clientFiles = ["APIGateway\(baseName)\(targetSuffix).swift", @@ -29,19 +47,21 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { "Throwing\(baseName)\(targetSuffix).swift"] let clientOutputPaths = clientFiles.map { clientDirectory.appending($0) } - let inputFile = context.package.directory.appending("smoke-framework-codegen.json") - // Specifying the input and output paths lets the build system know // when to invoke the command. let inputFiles = [inputFile] let outputFiles = clientOutputPaths // Construct the command arguments. - let commandArgs = [ + var commandArgs = [ "--base-file-path", context.package.directory.description, "--base-output-file-path", context.pluginWorkDirectory.description, "--generation-type", "codeGenClient" ] + + if let modelFilePathOverride = modelFilePathOverride { + commandArgs.append(contentsOf: ["--model-path", modelFilePathOverride]) + } // Append a command containing the information we generated. let command: Command = .buildCommand( @@ -53,4 +73,64 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { return [command] } + + private func getModelFilePathOverride(target: Target, configFilePath: String) throws -> String? { + let configFile = FileHandle(forReadingAtPath: configFilePath) + + let config: SmokeFrameworkCodeGen? + if let configData = configFile?.readDataToEndOfFile() { + config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) + } else { + config = nil + } + + // if the model is in a dependency + if let config = config, let modelProductDependency = config.modelProductDependency { + let dependencies: [Product] = target.dependencies.compactMap { dependency in + if case .product(let product) = dependency, product.name == modelProductDependency { + return product + } + + return nil + } + + // if there is no such dependency + guard let modelProduct = dependencies.first else { + throw PluginError.unknownModelPackageDependency(packageName: modelProductDependency) + } + + let modelTargetDependency = config.modelTargetDependency ?? modelProductDependency + + let filteredTargets = modelProduct.targets.filter { $0.name == modelTargetDependency } + guard let modelTarget = filteredTargets.first else { + throw PluginError.unknownModelTargetDependency(packageName: modelProductDependency, + targetName: modelTargetDependency) + } + + guard let modelTarget = modelTarget as? SourceModuleTarget else { + throw PluginError.sourceModuleTargetRequired(packageName: modelProductDependency, + targetName: modelTargetDependency, + type: type(of: modelTarget)) + } + + let targetDirectory: String + let rawTargetDirectory = modelTarget.directory.string + if !rawTargetDirectory.hasSuffix("/") { + targetDirectory = "\(rawTargetDirectory)/" + } else { + targetDirectory = rawTargetDirectory + } + + let filteredFiles = modelTarget.sourceFiles.filter { $0.path.string.dropFirst(targetDirectory.count) == config.modelFilePath } + guard let modelFile = filteredFiles.first else { + throw PluginError.unknownModelFilePath(packageName: modelProductDependency, + targetName: modelTargetDependency, + fileName: config.modelFilePath) + } + + return modelFile.path.string + } + + return nil + } } diff --git a/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift b/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift index b16479e..520350f 100644 --- a/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift @@ -3,8 +3,21 @@ import Foundation private let targetSuffix = "OperationsHTTP1" +enum PluginError: Error { + case unknownModelPackageDependency(packageName: String) + case unknownModelTargetDependency(packageName: String, targetName: String) + case sourceModuleTargetRequired(packageName: String, targetName: String, type: Target.Type) + case unknownModelFilePath(packageName: String, targetName: String, fileName: String) +} + @main struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { + struct SmokeFrameworkCodeGen: Codable { + let modelProductDependency: String? + let modelTargetDependency: String? + let modelFilePath: String + } + /// This plugin's implementation returns a single build command which /// calls `SmokeFrameworkApplicationGenerate` to generate the http1 protocol integration. func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { @@ -16,6 +29,10 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { if baseName.hasSuffix(targetSuffix) { baseName = String(baseName.dropLast(targetSuffix.count)) } + + let inputFile = context.package.directory.appending("smoke-framework-codegen.json") + + let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string) let http1Directory = sourcesDirectory.appending("\(baseName)\(targetSuffix)") @@ -24,20 +41,22 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { "\(baseName)OperationsHTTPOutput.swift", "\(baseName)PerInvocationContextInitializerProtocol.swift"] let http1OutputPaths = http1Files.map { http1Directory.appending($0) } - - let inputFile = context.package.directory.appending("smoke-framework-codegen.json") - + // Specifying the input and output paths lets the build system know // when to invoke the command. let inputFiles = [inputFile] let outputFiles = http1OutputPaths // Construct the command arguments. - let commandArgs = [ + var commandArgs = [ "--base-file-path", context.package.directory.description, "--base-output-file-path", context.pluginWorkDirectory.description, "--generation-type", "codeGenHttp1" ] + + if let modelFilePathOverride = modelFilePathOverride { + commandArgs.append(contentsOf: ["--model-path", modelFilePathOverride]) + } // Append a command containing the information we generated. let command: Command = .buildCommand( @@ -49,4 +68,64 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { return [command] } + + private func getModelFilePathOverride(target: Target, configFilePath: String) throws -> String? { + let configFile = FileHandle(forReadingAtPath: configFilePath) + + let config: SmokeFrameworkCodeGen? + if let configData = configFile?.readDataToEndOfFile() { + config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) + } else { + config = nil + } + + // if the model is in a dependency + if let config = config, let modelProductDependency = config.modelProductDependency { + let dependencies: [Product] = target.dependencies.compactMap { dependency in + if case .product(let product) = dependency, product.name == modelProductDependency { + return product + } + + return nil + } + + // if there is no such dependency + guard let modelProduct = dependencies.first else { + throw PluginError.unknownModelPackageDependency(packageName: modelProductDependency) + } + + let modelTargetDependency = config.modelTargetDependency ?? modelProductDependency + + let filteredTargets = modelProduct.targets.filter { $0.name == modelTargetDependency } + guard let modelTarget = filteredTargets.first else { + throw PluginError.unknownModelTargetDependency(packageName: modelProductDependency, + targetName: modelTargetDependency) + } + + guard let modelTarget = modelTarget as? SourceModuleTarget else { + throw PluginError.sourceModuleTargetRequired(packageName: modelProductDependency, + targetName: modelTargetDependency, + type: type(of: modelTarget)) + } + + let targetDirectory: String + let rawTargetDirectory = modelTarget.directory.string + if !rawTargetDirectory.hasSuffix("/") { + targetDirectory = "\(rawTargetDirectory)/" + } else { + targetDirectory = rawTargetDirectory + } + + let filteredFiles = modelTarget.sourceFiles.filter { $0.path.string.dropFirst(targetDirectory.count) == config.modelFilePath } + guard let modelFile = filteredFiles.first else { + throw PluginError.unknownModelFilePath(packageName: modelProductDependency, + targetName: modelTargetDependency, + fileName: config.modelFilePath) + } + + return modelFile.path.string + } + + return nil + } } diff --git a/Plugins/SmokeFrameworkGenerateModel/plugin.swift b/Plugins/SmokeFrameworkGenerateModel/plugin.swift index 450653f..1acfe3e 100644 --- a/Plugins/SmokeFrameworkGenerateModel/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateModel/plugin.swift @@ -3,8 +3,21 @@ import Foundation private let targetSuffix = "Model" +enum PluginError: Error { + case unknownModelPackageDependency(packageName: String) + case unknownModelTargetDependency(packageName: String, targetName: String) + case sourceModuleTargetRequired(packageName: String, targetName: String, type: Target.Type) + case unknownModelFilePath(packageName: String, targetName: String, fileName: String) +} + @main struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { + struct SmokeFrameworkCodeGen: Codable { + let modelProductDependency: String? + let modelTargetDependency: String? + let modelFilePath: String + } + /// This plugin's implementation returns a single build command which /// calls `SmokeFrameworkApplicationGenerate` to generate the service model. func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { @@ -17,6 +30,10 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { baseName = String(baseName.dropLast(targetSuffix.count)) } + let inputFile = context.package.directory.appending("smoke-framework-codegen.json") + + let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string) + let modelDirectory = sourcesDirectory.appending("\(baseName)\(targetSuffix)") let modelFiles = ["\(baseName)\(targetSuffix)Errors.swift", @@ -25,20 +42,22 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { "\(baseName)\(targetSuffix)Operations.swift", "\(baseName)\(targetSuffix)Types.swift"] let modelOutputPaths = modelFiles.map { modelDirectory.appending($0) } - - let inputFile = context.package.directory.appending("smoke-framework-codegen.json") - + // Specifying the input and output paths lets the build system know // when to invoke the command. let inputFiles = [inputFile] let outputFiles = modelOutputPaths // Construct the command arguments. - let commandArgs = [ + var commandArgs = [ "--base-file-path", context.package.directory.description, "--base-output-file-path", context.pluginWorkDirectory.description, "--generation-type", "codeGenModel" ] + + if let modelFilePathOverride = modelFilePathOverride { + commandArgs.append(contentsOf: ["--model-path", modelFilePathOverride]) + } // Append a command containing the information we generated. let command: Command = .buildCommand( @@ -50,4 +69,64 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { return [command] } + + private func getModelFilePathOverride(target: Target, configFilePath: String) throws -> String? { + let configFile = FileHandle(forReadingAtPath: configFilePath) + + let config: SmokeFrameworkCodeGen? + if let configData = configFile?.readDataToEndOfFile() { + config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) + } else { + config = nil + } + + // if the model is in a dependency + if let config = config, let modelProductDependency = config.modelProductDependency { + let dependencies: [Product] = target.dependencies.compactMap { dependency in + if case .product(let product) = dependency, product.name == modelProductDependency { + return product + } + + return nil + } + + // if there is no such dependency + guard let modelProduct = dependencies.first else { + throw PluginError.unknownModelPackageDependency(packageName: modelProductDependency) + } + + let modelTargetDependency = config.modelTargetDependency ?? modelProductDependency + + let filteredTargets = modelProduct.targets.filter { $0.name == modelTargetDependency } + guard let modelTarget = filteredTargets.first else { + throw PluginError.unknownModelTargetDependency(packageName: modelProductDependency, + targetName: modelTargetDependency) + } + + guard let modelTarget = modelTarget as? SourceModuleTarget else { + throw PluginError.sourceModuleTargetRequired(packageName: modelProductDependency, + targetName: modelTargetDependency, + type: type(of: modelTarget)) + } + + let targetDirectory: String + let rawTargetDirectory = modelTarget.directory.string + if !rawTargetDirectory.hasSuffix("/") { + targetDirectory = "\(rawTargetDirectory)/" + } else { + targetDirectory = rawTargetDirectory + } + + let filteredFiles = modelTarget.sourceFiles.filter { $0.path.string.dropFirst(targetDirectory.count) == config.modelFilePath } + guard let modelFile = filteredFiles.first else { + throw PluginError.unknownModelFilePath(packageName: modelProductDependency, + targetName: modelTargetDependency, + fileName: config.modelFilePath) + } + + return modelFile.path.string + } + + return nil + } } diff --git a/README.md b/README.md index 77ae304..dc14486 100644 --- a/README.md +++ b/README.md @@ -282,6 +282,79 @@ Update the generationType specified in the `smoke-framework-codegen.json` file t Manually run the generator. This should create a placeholder file in each of the Model, Client and Http1 Integration packages. Due to a current limitation of the SPM plugins for code generators, a placeholder Swift file is required in each package to avoid the package as being seen as empty. These files need to be a Swift file but doesn't require any particular contents. +# Using the generator as an SPM Plugin with an external model + +You can use the SPM plugin with a model defined in a seperate package by declaring that package as a dependency of the targets using the generator. + +## Step 1: Prepare the model package + +Setup the model package as a Swift package that declares the model file as a resource. + +``` +// swift-tools-version: 5.6 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "MyModelPackage", + products: [ + .library( + name: "MyModelPackage", + targets: ["MyModelPackage"]), + ], + targets: [ + .target( + name: "MyModelPackage", + dependencies: [], + path: "configuration/api", + resources: [.copy("Swagger.yaml")]), + ] +) +``` + +**Note:** If the target in this package doesn't define any Swift files, you will need to create an empty Swift file due to a current limitation in SwiftPM. + +## Step 2: Add the model package as a dependency + +Add this model package as a dependency of the package with your service code. + +``` + .package(url: "https://github.com/DonnaNoble/my-model-package.git", from: "1.0.0"), +``` + +and then as a dependency of any targets you are using the SPM plugin for. + +``` +.target( + name: "EmptyExampleClient", dependencies: [ + .target(name: "EmptyExampleModel"), + .product(name: "SmokeOperationsHTTP1", package: "smoke-framework"), + .product(name: "SmokeAWSHttp", package: "smoke-aws"), + .product(name: "MyModelPackage", package: "my-model-package"), + ], + plugins: [ + .plugin(name: "SmokeFrameworkGenerateClient", package: "smoke-framework-application-generate") + ] +), +``` + +## Step 3: Update the code generation configuration file + +Finally, in the `smoke-framework-codegen.json` configuration file, add the `modelProductDependency` field to specify where the model is located. + +``` +{ + "baseName" : "EmptyService", + "modelProductDependency": "MyModelPackage", + "modelFilePath" : "Swagger.yaml", + "generationType" : "serverUpdateWithPlugin", + ... +``` + +By default, the name specified by the `modelProductDependency` field will be used for both the Product and the Target used by that Product +where the model file is located. If the Target has a different name, this can be specified with the `modelTargetDependency` field. + ## License This library is licensed under the Apache 2.0 License. From eff332dce8c05d6f180d912fa2c637605839be2c Mon Sep 17 00:00:00 2001 From: Simon Pilkington Date: Thu, 4 Aug 2022 12:52:02 +1000 Subject: [PATCH 2/4] Add the ability to use different models for different targets. --- .../SmokeFrameworkGenerateClient/plugin.swift | 82 +++++++++++++++---- .../SmokeFrameworkGenerateHttp1/plugin.swift | 82 +++++++++++++++---- .../SmokeFrameworkGenerateModel/plugin.swift | 82 +++++++++++++++---- .../SmokeFrameworkCodeGen.swift | 2 +- 4 files changed, 193 insertions(+), 55 deletions(-) diff --git a/Plugins/SmokeFrameworkGenerateClient/plugin.swift b/Plugins/SmokeFrameworkGenerateClient/plugin.swift index 5420bb1..bb92147 100644 --- a/Plugins/SmokeFrameworkGenerateClient/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateClient/plugin.swift @@ -8,16 +8,38 @@ enum PluginError: Error { case unknownModelTargetDependency(packageName: String, targetName: String) case sourceModuleTargetRequired(packageName: String, targetName: String, type: Target.Type) case unknownModelFilePath(packageName: String, targetName: String, fileName: String) + case missingConfigFile(expectedPath: String) + case missingModelLocation(target: String) } @main struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { - struct SmokeFrameworkCodeGen: Codable { + struct ModelLocation: Decodable { let modelProductDependency: String? let modelTargetDependency: String? let modelFilePath: String } + struct ModelLocations: Decodable { + let `default`: ModelLocation? + let targetMap: [String: ModelLocation] + + enum CodingKeys: String, CodingKey { + case `default` + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + self.`default` = try values.decodeIfPresent(ModelLocation.self, forKey: .default) + self.targetMap = try [String: ModelLocation].init(from: decoder) + } + } + + struct SmokeFrameworkCodeGen: Decodable { + let modelLocations: ModelLocations? + let modelFilePath: String? // legacy location + } + /// This plugin's implementation returns a single build command which /// calls `SmokeFrameworkApplicationGenerate` to generate the service client. func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { @@ -32,7 +54,8 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { let inputFile = context.package.directory.appending("smoke-framework-codegen.json") - let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string) + let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string, + baseFilePath: context.package.directory) let clientDirectory = sourcesDirectory.appending("\(baseName)\(targetSuffix)") @@ -53,15 +76,12 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { let outputFiles = clientOutputPaths // Construct the command arguments. - var commandArgs = [ + let commandArgs = [ "--base-file-path", context.package.directory.description, "--base-output-file-path", context.pluginWorkDirectory.description, - "--generation-type", "codeGenClient" + "--generation-type", "codeGenClient", + "--model-path", modelFilePathOverride ] - - if let modelFilePathOverride = modelFilePathOverride { - commandArgs.append(contentsOf: ["--model-path", modelFilePathOverride]) - } // Append a command containing the information we generated. let command: Command = .buildCommand( @@ -74,18 +94,43 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { return [command] } - private func getModelFilePathOverride(target: Target, configFilePath: String) throws -> String? { + private func getModelFilePathOverride(target: Target, configFilePath: String, + baseFilePath: PackagePlugin.Path) throws -> String { let configFile = FileHandle(forReadingAtPath: configFilePath) - let config: SmokeFrameworkCodeGen? - if let configData = configFile?.readDataToEndOfFile() { - config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) + guard let configData = configFile?.readDataToEndOfFile() else { + throw PluginError.missingConfigFile(expectedPath: configFilePath) + } + + let config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) + + // find the model for the current target + let filteredModelLocations = config.modelLocations?.targetMap.compactMap { (targetName, modelLocation) -> ModelLocation? in + if targetName == target.name { + return modelLocation + } + + return nil + } + + let modelLocation: ModelLocation + if let theModelLocation = filteredModelLocations?.first { + modelLocation = theModelLocation + } else if let theModelLocation = config.modelLocations?.default { + modelLocation = theModelLocation + } else if let modelFilePath = config.modelFilePath { + modelLocation = ModelLocation(modelProductDependency: nil, modelTargetDependency: nil, modelFilePath: modelFilePath) } else { - config = nil + throw PluginError.missingModelLocation(target: target.name) } + return try getModelFilePathOverride(target: target, modelLocation: modelLocation, baseFilePath: baseFilePath) + } + + private func getModelFilePathOverride(target: Target, modelLocation: ModelLocation, + baseFilePath: PackagePlugin.Path) throws -> String { // if the model is in a dependency - if let config = config, let modelProductDependency = config.modelProductDependency { + if let modelProductDependency = modelLocation.modelProductDependency { let dependencies: [Product] = target.dependencies.compactMap { dependency in if case .product(let product) = dependency, product.name == modelProductDependency { return product @@ -99,7 +144,7 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { throw PluginError.unknownModelPackageDependency(packageName: modelProductDependency) } - let modelTargetDependency = config.modelTargetDependency ?? modelProductDependency + let modelTargetDependency = modelLocation.modelTargetDependency ?? modelProductDependency let filteredTargets = modelProduct.targets.filter { $0.name == modelTargetDependency } guard let modelTarget = filteredTargets.first else { @@ -121,16 +166,17 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { targetDirectory = rawTargetDirectory } - let filteredFiles = modelTarget.sourceFiles.filter { $0.path.string.dropFirst(targetDirectory.count) == config.modelFilePath } + let filteredFiles = modelTarget.sourceFiles.filter { $0.path.string.dropFirst(targetDirectory.count) == modelLocation.modelFilePath } guard let modelFile = filteredFiles.first else { throw PluginError.unknownModelFilePath(packageName: modelProductDependency, targetName: modelTargetDependency, - fileName: config.modelFilePath) + fileName: modelLocation.modelFilePath) } return modelFile.path.string } - return nil + // the model is local to the package + return baseFilePath.appending(modelLocation.modelFilePath).description } } diff --git a/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift b/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift index 520350f..6ae03ab 100644 --- a/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift @@ -8,16 +8,38 @@ enum PluginError: Error { case unknownModelTargetDependency(packageName: String, targetName: String) case sourceModuleTargetRequired(packageName: String, targetName: String, type: Target.Type) case unknownModelFilePath(packageName: String, targetName: String, fileName: String) + case missingConfigFile(expectedPath: String) + case missingModelLocation(target: String) } @main struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { - struct SmokeFrameworkCodeGen: Codable { + struct ModelLocation: Decodable { let modelProductDependency: String? let modelTargetDependency: String? let modelFilePath: String } + struct ModelLocations: Decodable { + let `default`: ModelLocation? + let targetMap: [String: ModelLocation] + + enum CodingKeys: String, CodingKey { + case `default` + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + self.`default` = try values.decodeIfPresent(ModelLocation.self, forKey: .default) + self.targetMap = try [String: ModelLocation].init(from: decoder) + } + } + + struct SmokeFrameworkCodeGen: Decodable { + let modelLocations: ModelLocations? + let modelFilePath: String? // legacy location + } + /// This plugin's implementation returns a single build command which /// calls `SmokeFrameworkApplicationGenerate` to generate the http1 protocol integration. func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { @@ -32,7 +54,8 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { let inputFile = context.package.directory.appending("smoke-framework-codegen.json") - let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string) + let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string, + baseFilePath: context.package.directory) let http1Directory = sourcesDirectory.appending("\(baseName)\(targetSuffix)") @@ -48,15 +71,12 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { let outputFiles = http1OutputPaths // Construct the command arguments. - var commandArgs = [ + let commandArgs = [ "--base-file-path", context.package.directory.description, "--base-output-file-path", context.pluginWorkDirectory.description, - "--generation-type", "codeGenHttp1" + "--generation-type", "codeGenHttp1", + "--model-path", modelFilePathOverride ] - - if let modelFilePathOverride = modelFilePathOverride { - commandArgs.append(contentsOf: ["--model-path", modelFilePathOverride]) - } // Append a command containing the information we generated. let command: Command = .buildCommand( @@ -69,18 +89,43 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { return [command] } - private func getModelFilePathOverride(target: Target, configFilePath: String) throws -> String? { + private func getModelFilePathOverride(target: Target, configFilePath: String, + baseFilePath: PackagePlugin.Path) throws -> String { let configFile = FileHandle(forReadingAtPath: configFilePath) - let config: SmokeFrameworkCodeGen? - if let configData = configFile?.readDataToEndOfFile() { - config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) + guard let configData = configFile?.readDataToEndOfFile() else { + throw PluginError.missingConfigFile(expectedPath: configFilePath) + } + + let config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) + + // find the model for the current target + let filteredModelLocations = config.modelLocations?.targetMap.compactMap { (targetName, modelLocation) -> ModelLocation? in + if targetName == target.name { + return modelLocation + } + + return nil + } + + let modelLocation: ModelLocation + if let theModelLocation = filteredModelLocations?.first { + modelLocation = theModelLocation + } else if let theModelLocation = config.modelLocations?.default { + modelLocation = theModelLocation + } else if let modelFilePath = config.modelFilePath { + modelLocation = ModelLocation(modelProductDependency: nil, modelTargetDependency: nil, modelFilePath: modelFilePath) } else { - config = nil + throw PluginError.missingModelLocation(target: target.name) } + return try getModelFilePathOverride(target: target, modelLocation: modelLocation, baseFilePath: baseFilePath) + } + + private func getModelFilePathOverride(target: Target, modelLocation: ModelLocation, + baseFilePath: PackagePlugin.Path) throws -> String { // if the model is in a dependency - if let config = config, let modelProductDependency = config.modelProductDependency { + if let modelProductDependency = modelLocation.modelProductDependency { let dependencies: [Product] = target.dependencies.compactMap { dependency in if case .product(let product) = dependency, product.name == modelProductDependency { return product @@ -94,7 +139,7 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { throw PluginError.unknownModelPackageDependency(packageName: modelProductDependency) } - let modelTargetDependency = config.modelTargetDependency ?? modelProductDependency + let modelTargetDependency = modelLocation.modelTargetDependency ?? modelProductDependency let filteredTargets = modelProduct.targets.filter { $0.name == modelTargetDependency } guard let modelTarget = filteredTargets.first else { @@ -116,16 +161,17 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { targetDirectory = rawTargetDirectory } - let filteredFiles = modelTarget.sourceFiles.filter { $0.path.string.dropFirst(targetDirectory.count) == config.modelFilePath } + let filteredFiles = modelTarget.sourceFiles.filter { $0.path.string.dropFirst(targetDirectory.count) == modelLocation.modelFilePath } guard let modelFile = filteredFiles.first else { throw PluginError.unknownModelFilePath(packageName: modelProductDependency, targetName: modelTargetDependency, - fileName: config.modelFilePath) + fileName: modelLocation.modelFilePath) } return modelFile.path.string } - return nil + // the model is local to the package + return baseFilePath.appending(modelLocation.modelFilePath).description } } diff --git a/Plugins/SmokeFrameworkGenerateModel/plugin.swift b/Plugins/SmokeFrameworkGenerateModel/plugin.swift index 1acfe3e..528a439 100644 --- a/Plugins/SmokeFrameworkGenerateModel/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateModel/plugin.swift @@ -8,16 +8,38 @@ enum PluginError: Error { case unknownModelTargetDependency(packageName: String, targetName: String) case sourceModuleTargetRequired(packageName: String, targetName: String, type: Target.Type) case unknownModelFilePath(packageName: String, targetName: String, fileName: String) + case missingConfigFile(expectedPath: String) + case missingModelLocation(target: String) } @main struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { - struct SmokeFrameworkCodeGen: Codable { + struct ModelLocation: Decodable { let modelProductDependency: String? let modelTargetDependency: String? let modelFilePath: String } + struct ModelLocations: Decodable { + let `default`: ModelLocation? + let targetMap: [String: ModelLocation] + + enum CodingKeys: String, CodingKey { + case `default` + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + self.`default` = try values.decodeIfPresent(ModelLocation.self, forKey: .default) + self.targetMap = try [String: ModelLocation].init(from: decoder) + } + } + + struct SmokeFrameworkCodeGen: Decodable { + let modelLocations: ModelLocations? + let modelFilePath: String? // legacy location + } + /// This plugin's implementation returns a single build command which /// calls `SmokeFrameworkApplicationGenerate` to generate the service model. func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] { @@ -32,7 +54,8 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { let inputFile = context.package.directory.appending("smoke-framework-codegen.json") - let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string) + let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string, + baseFilePath: context.package.directory) let modelDirectory = sourcesDirectory.appending("\(baseName)\(targetSuffix)") @@ -49,15 +72,12 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { let outputFiles = modelOutputPaths // Construct the command arguments. - var commandArgs = [ + let commandArgs = [ "--base-file-path", context.package.directory.description, "--base-output-file-path", context.pluginWorkDirectory.description, - "--generation-type", "codeGenModel" + "--generation-type", "codeGenModel", + "--model-path", modelFilePathOverride ] - - if let modelFilePathOverride = modelFilePathOverride { - commandArgs.append(contentsOf: ["--model-path", modelFilePathOverride]) - } // Append a command containing the information we generated. let command: Command = .buildCommand( @@ -70,18 +90,43 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { return [command] } - private func getModelFilePathOverride(target: Target, configFilePath: String) throws -> String? { + private func getModelFilePathOverride(target: Target, configFilePath: String, + baseFilePath: PackagePlugin.Path) throws -> String { let configFile = FileHandle(forReadingAtPath: configFilePath) - let config: SmokeFrameworkCodeGen? - if let configData = configFile?.readDataToEndOfFile() { - config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) + guard let configData = configFile?.readDataToEndOfFile() else { + throw PluginError.missingConfigFile(expectedPath: configFilePath) + } + + let config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) + + // find the model for the current target + let filteredModelLocations = config.modelLocations?.targetMap.compactMap { (targetName, modelLocation) -> ModelLocation? in + if targetName == target.name { + return modelLocation + } + + return nil + } + + let modelLocation: ModelLocation + if let theModelLocation = filteredModelLocations?.first { + modelLocation = theModelLocation + } else if let theModelLocation = config.modelLocations?.default { + modelLocation = theModelLocation + } else if let modelFilePath = config.modelFilePath { + modelLocation = ModelLocation(modelProductDependency: nil, modelTargetDependency: nil, modelFilePath: modelFilePath) } else { - config = nil + throw PluginError.missingModelLocation(target: target.name) } + return try getModelFilePathOverride(target: target, modelLocation: modelLocation, baseFilePath: baseFilePath) + } + + private func getModelFilePathOverride(target: Target, modelLocation: ModelLocation, + baseFilePath: PackagePlugin.Path) throws -> String { // if the model is in a dependency - if let config = config, let modelProductDependency = config.modelProductDependency { + if let modelProductDependency = modelLocation.modelProductDependency { let dependencies: [Product] = target.dependencies.compactMap { dependency in if case .product(let product) = dependency, product.name == modelProductDependency { return product @@ -95,7 +140,7 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { throw PluginError.unknownModelPackageDependency(packageName: modelProductDependency) } - let modelTargetDependency = config.modelTargetDependency ?? modelProductDependency + let modelTargetDependency = modelLocation.modelTargetDependency ?? modelProductDependency let filteredTargets = modelProduct.targets.filter { $0.name == modelTargetDependency } guard let modelTarget = filteredTargets.first else { @@ -117,16 +162,17 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { targetDirectory = rawTargetDirectory } - let filteredFiles = modelTarget.sourceFiles.filter { $0.path.string.dropFirst(targetDirectory.count) == config.modelFilePath } + let filteredFiles = modelTarget.sourceFiles.filter { $0.path.string.dropFirst(targetDirectory.count) == modelLocation.modelFilePath } guard let modelFile = filteredFiles.first else { throw PluginError.unknownModelFilePath(packageName: modelProductDependency, targetName: modelTargetDependency, - fileName: config.modelFilePath) + fileName: modelLocation.modelFilePath) } return modelFile.path.string } - return nil + // the model is local to the package + return baseFilePath.appending(modelLocation.modelFilePath).description } } diff --git a/Sources/SmokeFrameworkApplicationGenerate/SmokeFrameworkCodeGen.swift b/Sources/SmokeFrameworkApplicationGenerate/SmokeFrameworkCodeGen.swift index f6a7dcb..41105da 100644 --- a/Sources/SmokeFrameworkApplicationGenerate/SmokeFrameworkCodeGen.swift +++ b/Sources/SmokeFrameworkApplicationGenerate/SmokeFrameworkCodeGen.swift @@ -25,7 +25,7 @@ enum ModelFormat: String, Codable { } struct SmokeFrameworkCodeGen: Codable { - let modelFilePath: String + let modelFilePath: String? let modelFormat: ModelFormat? let baseName: String let applicationSuffix: String? From 7e50fc5449fdb15124f4f1356f70ed6b25b0e046 Mon Sep 17 00:00:00 2001 From: Simon Pilkington Date: Thu, 4 Aug 2022 16:12:35 +1000 Subject: [PATCH 3/4] Don't guess baseName. --- .../SmokeFrameworkGenerateClient/plugin.swift | 26 +++++++++---------- .../SmokeFrameworkGenerateHttp1/plugin.swift | 26 +++++++++---------- .../SmokeFrameworkGenerateModel/plugin.swift | 26 +++++++++---------- 3 files changed, 36 insertions(+), 42 deletions(-) diff --git a/Plugins/SmokeFrameworkGenerateClient/plugin.swift b/Plugins/SmokeFrameworkGenerateClient/plugin.swift index bb92147..19fc34b 100644 --- a/Plugins/SmokeFrameworkGenerateClient/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateClient/plugin.swift @@ -36,6 +36,7 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { } struct SmokeFrameworkCodeGen: Decodable { + let baseName: String let modelLocations: ModelLocations? let modelFilePath: String? // legacy location } @@ -47,14 +48,19 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { let smokeFrameworkApplicationGenerateTool = try context.tool(named: "SmokeFrameworkApplicationGenerate") let sourcesDirectory = context.pluginWorkDirectory.appending("Sources") - var baseName = target.name - if baseName.hasSuffix(targetSuffix) { - baseName = String(baseName.dropLast(targetSuffix.count)) + let inputFile = context.package.directory.appending("smoke-framework-codegen.json") + let configFilePath = inputFile.string + let configFile = FileHandle(forReadingAtPath: configFilePath) + + guard let configData = configFile?.readDataToEndOfFile() else { + throw PluginError.missingConfigFile(expectedPath: configFilePath) } - let inputFile = context.package.directory.appending("smoke-framework-codegen.json") + let config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) - let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string, + let baseName = config.baseName + + let modelFilePathOverride = try getModelFilePathOverride(target: target, config: config, baseFilePath: context.package.directory) let clientDirectory = sourcesDirectory.appending("\(baseName)\(targetSuffix)") @@ -94,16 +100,8 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { return [command] } - private func getModelFilePathOverride(target: Target, configFilePath: String, + private func getModelFilePathOverride(target: Target, config: SmokeFrameworkCodeGen, baseFilePath: PackagePlugin.Path) throws -> String { - let configFile = FileHandle(forReadingAtPath: configFilePath) - - guard let configData = configFile?.readDataToEndOfFile() else { - throw PluginError.missingConfigFile(expectedPath: configFilePath) - } - - let config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) - // find the model for the current target let filteredModelLocations = config.modelLocations?.targetMap.compactMap { (targetName, modelLocation) -> ModelLocation? in if targetName == target.name { diff --git a/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift b/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift index 6ae03ab..ed53fe0 100644 --- a/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift @@ -36,6 +36,7 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { } struct SmokeFrameworkCodeGen: Decodable { + let baseName: String let modelLocations: ModelLocations? let modelFilePath: String? // legacy location } @@ -47,14 +48,19 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { let smokeFrameworkApplicationGenerateTool = try context.tool(named: "SmokeFrameworkApplicationGenerate") let sourcesDirectory = context.pluginWorkDirectory.appending("Sources") - var baseName = target.name - if baseName.hasSuffix(targetSuffix) { - baseName = String(baseName.dropLast(targetSuffix.count)) + let inputFile = context.package.directory.appending("smoke-framework-codegen.json") + let configFilePath = inputFile.string + let configFile = FileHandle(forReadingAtPath: configFilePath) + + guard let configData = configFile?.readDataToEndOfFile() else { + throw PluginError.missingConfigFile(expectedPath: configFilePath) } - let inputFile = context.package.directory.appending("smoke-framework-codegen.json") + let config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) - let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string, + let baseName = config.baseName + + let modelFilePathOverride = try getModelFilePathOverride(target: target, config: config, baseFilePath: context.package.directory) let http1Directory = sourcesDirectory.appending("\(baseName)\(targetSuffix)") @@ -89,16 +95,8 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { return [command] } - private func getModelFilePathOverride(target: Target, configFilePath: String, + private func getModelFilePathOverride(target: Target, config: SmokeFrameworkCodeGen, baseFilePath: PackagePlugin.Path) throws -> String { - let configFile = FileHandle(forReadingAtPath: configFilePath) - - guard let configData = configFile?.readDataToEndOfFile() else { - throw PluginError.missingConfigFile(expectedPath: configFilePath) - } - - let config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) - // find the model for the current target let filteredModelLocations = config.modelLocations?.targetMap.compactMap { (targetName, modelLocation) -> ModelLocation? in if targetName == target.name { diff --git a/Plugins/SmokeFrameworkGenerateModel/plugin.swift b/Plugins/SmokeFrameworkGenerateModel/plugin.swift index 528a439..6a46487 100644 --- a/Plugins/SmokeFrameworkGenerateModel/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateModel/plugin.swift @@ -36,6 +36,7 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { } struct SmokeFrameworkCodeGen: Decodable { + let baseName: String let modelLocations: ModelLocations? let modelFilePath: String? // legacy location } @@ -47,14 +48,19 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { let smokeFrameworkApplicationGenerateTool = try context.tool(named: "SmokeFrameworkApplicationGenerate") let sourcesDirectory = context.pluginWorkDirectory.appending("Sources") - var baseName = target.name - if baseName.hasSuffix(targetSuffix) { - baseName = String(baseName.dropLast(targetSuffix.count)) + let inputFile = context.package.directory.appending("smoke-framework-codegen.json") + let configFilePath = inputFile.string + let configFile = FileHandle(forReadingAtPath: configFilePath) + + guard let configData = configFile?.readDataToEndOfFile() else { + throw PluginError.missingConfigFile(expectedPath: configFilePath) } - let inputFile = context.package.directory.appending("smoke-framework-codegen.json") + let config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) - let modelFilePathOverride = try getModelFilePathOverride(target: target, configFilePath: inputFile.string, + let baseName = config.baseName + + let modelFilePathOverride = try getModelFilePathOverride(target: target, config: config, baseFilePath: context.package.directory) let modelDirectory = sourcesDirectory.appending("\(baseName)\(targetSuffix)") @@ -90,16 +96,8 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { return [command] } - private func getModelFilePathOverride(target: Target, configFilePath: String, + private func getModelFilePathOverride(target: Target, config: SmokeFrameworkCodeGen, baseFilePath: PackagePlugin.Path) throws -> String { - let configFile = FileHandle(forReadingAtPath: configFilePath) - - guard let configData = configFile?.readDataToEndOfFile() else { - throw PluginError.missingConfigFile(expectedPath: configFilePath) - } - - let config = try JSONDecoder().decode(SmokeFrameworkCodeGen.self, from: configData) - // find the model for the current target let filteredModelLocations = config.modelLocations?.targetMap.compactMap { (targetName, modelLocation) -> ModelLocation? in if targetName == target.name { From 9124670c858075af0036d98cf40d68fc51951710 Mon Sep 17 00:00:00 2001 From: Simon Pilkington Date: Fri, 5 Aug 2022 06:26:11 +1000 Subject: [PATCH 4/4] Update README. --- .../SmokeFrameworkGenerateClient/plugin.swift | 10 ++---- .../SmokeFrameworkGenerateHttp1/plugin.swift | 10 ++---- .../SmokeFrameworkGenerateModel/plugin.swift | 10 ++---- README.md | 36 +++++++++++++++---- 4 files changed, 36 insertions(+), 30 deletions(-) diff --git a/Plugins/SmokeFrameworkGenerateClient/plugin.swift b/Plugins/SmokeFrameworkGenerateClient/plugin.swift index 19fc34b..d079037 100644 --- a/Plugins/SmokeFrameworkGenerateClient/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateClient/plugin.swift @@ -103,16 +103,10 @@ struct SmokeFrameworkGenerateClientPlugin: BuildToolPlugin { private func getModelFilePathOverride(target: Target, config: SmokeFrameworkCodeGen, baseFilePath: PackagePlugin.Path) throws -> String { // find the model for the current target - let filteredModelLocations = config.modelLocations?.targetMap.compactMap { (targetName, modelLocation) -> ModelLocation? in - if targetName == target.name { - return modelLocation - } - - return nil - } + let targetModelLocationOptional = config.modelLocations?.targetMap[target.name] let modelLocation: ModelLocation - if let theModelLocation = filteredModelLocations?.first { + if let theModelLocation = targetModelLocationOptional { modelLocation = theModelLocation } else if let theModelLocation = config.modelLocations?.default { modelLocation = theModelLocation diff --git a/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift b/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift index ed53fe0..63a209b 100644 --- a/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateHttp1/plugin.swift @@ -98,16 +98,10 @@ struct SmokeFrameworkGenerateHttp1Plugin: BuildToolPlugin { private func getModelFilePathOverride(target: Target, config: SmokeFrameworkCodeGen, baseFilePath: PackagePlugin.Path) throws -> String { // find the model for the current target - let filteredModelLocations = config.modelLocations?.targetMap.compactMap { (targetName, modelLocation) -> ModelLocation? in - if targetName == target.name { - return modelLocation - } - - return nil - } + let targetModelLocationOptional = config.modelLocations?.targetMap[target.name] let modelLocation: ModelLocation - if let theModelLocation = filteredModelLocations?.first { + if let theModelLocation = targetModelLocationOptional { modelLocation = theModelLocation } else if let theModelLocation = config.modelLocations?.default { modelLocation = theModelLocation diff --git a/Plugins/SmokeFrameworkGenerateModel/plugin.swift b/Plugins/SmokeFrameworkGenerateModel/plugin.swift index 6a46487..a53d5f7 100644 --- a/Plugins/SmokeFrameworkGenerateModel/plugin.swift +++ b/Plugins/SmokeFrameworkGenerateModel/plugin.swift @@ -99,16 +99,10 @@ struct SmokeFrameworkGenerateModelPlugin: BuildToolPlugin { private func getModelFilePathOverride(target: Target, config: SmokeFrameworkCodeGen, baseFilePath: PackagePlugin.Path) throws -> String { // find the model for the current target - let filteredModelLocations = config.modelLocations?.targetMap.compactMap { (targetName, modelLocation) -> ModelLocation? in - if targetName == target.name { - return modelLocation - } - - return nil - } + let targetModelLocationOptional = config.modelLocations?.targetMap[target.name] let modelLocation: ModelLocation - if let theModelLocation = filteredModelLocations?.first { + if let theModelLocation = targetModelLocationOptional { modelLocation = theModelLocation } else if let theModelLocation = config.modelLocations?.default { modelLocation = theModelLocation diff --git a/README.md b/README.md index dc14486..4a5b420 100644 --- a/README.md +++ b/README.md @@ -341,15 +341,39 @@ and then as a dependency of any targets you are using the SPM plugin for. ## Step 3: Update the code generation configuration file -Finally, in the `smoke-framework-codegen.json` configuration file, add the `modelProductDependency` field to specify where the model is located. +Finally, in the `smoke-framework-codegen.json` configuration file, add a `modelsLocation` block to specify where the model is +located, removing the `modelFilePath` field at the top level. ``` { - "baseName" : "EmptyService", - "modelProductDependency": "MyModelPackage", - "modelFilePath" : "Swagger.yaml", - "generationType" : "serverUpdateWithPlugin", - ... + "baseName" : "EmptyService", + "modelLocations": { + "default": { + "modelProductDependency": "MyModelPackage", + "modelFilePath" : "Swagger.yaml" + } + }, + ... +} +``` + +If you need to specify a separate model path for a particular target, you can also add this in the `modelsLocation` block. + +``` +{ + "baseName" : "EmptyService", + "modelLocations": { + "EmptyServiceExternalModel": { + "modelProductDependency": "MyModelPackage2", + "modelFilePath" : "Swagger.yaml" + } + "default": { + "modelProductDependency": "MyModelPackage", + "modelFilePath" : "Swagger.yaml" + } + }, + ... +} ``` By default, the name specified by the `modelProductDependency` field will be used for both the Product and the Target used by that Product