Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move Generated Client to separate module #486

Open
wants to merge 21 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
5cd8d77
Reconfigure OpenAPI generation to output a Swift Package, simplify do…
andrewdmontgomery Oct 4, 2024
110ebc2
Setup local package dependency
andrewdmontgomery Oct 4, 2024
68fd36e
Export OpenAPI types
andrewdmontgomery Oct 4, 2024
667a429
Refactor openapi-generator call
andrewdmontgomery Oct 8, 2024
707bd37
Update generated client
andrewdmontgomery Oct 8, 2024
32f8dd8
Rename OpenAPIClient --> GravatarOpenAPIClient
andrewdmontgomery Oct 8, 2024
283a10f
Match iOS deployment targets for all podspecs
andrewdmontgomery Oct 8, 2024
944c5d6
Update Makefile generation of GravatarOpenAPIClient.podspec
andrewdmontgomery Oct 8, 2024
3c19a1e
Revert project.yml version
andrewdmontgomery Oct 8, 2024
024e041
Stop ignoring the `openapi-generator` directory, since we don't need …
andrewdmontgomery Oct 8, 2024
6e3d2a4
Move GravatarOpenAPIClient into module (without third-party dep)
andrewdmontgomery Oct 9, 2024
84eeb1f
Stop tracking some files
andrewdmontgomery Oct 9, 2024
01c459f
Removes unnecessary `install-and-generate` Makefile task
andrewdmontgomery Oct 10, 2024
9c5b7e4
Update GravatarOpenAPIClient.podspec with path to module
andrewdmontgomery Oct 10, 2024
0b6d4dc
Update the validate-pods.sh script to check the new podspec
andrewdmontgomery Oct 10, 2024
c6a8e96
Update pipeline to publish the new podspec
andrewdmontgomery Oct 10, 2024
d28429d
Update Demo project Package.resolved
andrewdmontgomery Oct 10, 2024
90810ab
Merge branch 'release/3.0.0' into andrewdmontgomery/OpenAPI-with-SPM
andrewdmontgomery Oct 10, 2024
e1e7191
Regenerate SetEmailAvatarRequest
andrewdmontgomery Oct 10, 2024
93446f7
Fix typo in `generate` Makefile command
andrewdmontgomery Oct 10, 2024
b3b9f4c
Update .openapi-generator-ignore to generate only `Models` dir
andrewdmontgomery Oct 10, 2024
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
3 changes: 3 additions & 0 deletions .buildkite/commands/validate-pods.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ validate_podspec --allow-warnings Gravatar.podspec

echo "--- :cocoapods: Validate GravatarUI.podspec"
validate_podspec --allow-warnings GravatarUI.podspec

echo "--- :cocoapods: Validate GravatarOpenAPIClient.podspec"
validate_podspec --allow-warnings GravatarOpenAPIClient.podspec
1 change: 1 addition & 0 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ steps:
command: |
.buildkite/commands/publish-pod.sh "Gravatar.podspec"
.buildkite/commands/publish-pod.sh "GravatarUI.podspec"
.buildkite/commands/publish-pod.sh "GravatarOpenAPIClient.podspec"
plugins: [$CI_TOOLKIT]
depends_on:
- "test"
Expand Down
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ fastlane/README.md
fastlane/report.xml
fastlane/test_output

# OpenAPI
openapi/GravatarOpenAPIClient/
!openapi/GravatarOpenAPIClient/.openapi-generator/
!openapi/GravatarOpenAPIClient/.openapi-generator-ignore
!openapi/GravatarOpenAPIClient/docs/
!openapi/GravatarOpenAPIClient/GravatarOpenAPIClient.podspec
!openapi/GravatarOpenAPIClient/Package.swift
!openapi/GravatarOpenAPIClient/project.yml
!openapi/GravatarOpenAPIClient/README.md

# Other
openapi-generator/
Demo/Demo/Secrets.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "89aad130e119d7a4b1dea1ad42b23e52b29cf016999eed5d14af43b0dc276ac1",
"originHash" : "bf24fbca0585c9e7707869442e833d48e8fbe49f5e904fe58370244d9f54521e",
"pins" : [
{
"identity" : "swift-snapshot-testing",
Expand Down
3 changes: 2 additions & 1 deletion Gravatar.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ Pod::Spec.new do |s|

s.swift_versions = Gravatar::SWIFT_VERSIONS

ios_deployment_target = '15.0'
ios_deployment_target = Gravatar::IOS_DEPLOYMENT_TARGET

s.ios.deployment_target = ios_deployment_target

s.dependency 'GravatarOpenAPIClient', s.version.to_s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIU, GravatarOpenAPIClient's version is Gravatar's version. This makes me wonder what happens when the specs change and we re-generate GravatarOpenAPIClient. When I use the new models in Gravatar what should i set here so that cocoapods also builds fine in CI...

s.source_files = 'Sources/Gravatar/**/*.swift'
s.resource_bundles = {
'Gravatar' => ['Sources/Gravatar/Resources/*.plist']
Expand Down
23 changes: 23 additions & 0 deletions GravatarOpenAPIClient.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

require_relative 'version'

Pod::Spec.new do |s|
s.name = 'GravatarOpenAPIClient'
s.summary = 'A Gravatar OpenAPI Client'
s.version = Gravatar::VERSION

s.swift_versions = Gravatar::SWIFT_VERSIONS

# Match the deployment target of Gravatar in order to satisfy `pod lib lint`
s.ios.deployment_target = Gravatar::IOS_DEPLOYMENT_TARGET

s.homepage = 'https://gravatar.com'
s.license = { type: 'Mozilla Public License v2', file: 'LICENSE.md' }
s.authors = 'Automattic, Inc.'
s.source = { :git => 'https://github.com/Automattic/Gravatar-SDK-iOS.git', :tag => s.version.to_s }

s.documentation_url = 'https://automattic.github.io/Gravatar-SDK-iOS/gravatar'

s.source_files = 'Sources/GravatarOpenAPIClient/**/*.swift'
end
2 changes: 1 addition & 1 deletion GravatarUI.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Pod::Spec.new do |s|

s.swift_versions = Gravatar::SWIFT_VERSIONS

ios_deployment_target = '15.0'
ios_deployment_target = Gravatar::IOS_DEPLOYMENT_TARGET
s.ios.deployment_target = ios_deployment_target

s.source_files = 'Sources/GravatarUI/**/*.swift'
Expand Down
53 changes: 27 additions & 26 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ SWIFTFORMAT_CACHE = ~/Library/Caches/com.charcoaldesign.swiftformat
OPENAPI_GENERATOR_GIT_URL ?= https://github.com/openapitools/openapi-generator
OPENAPI_GENERATOR_GIT_TAG ?= v7.5.0
OPENAPI_GENERATOR_CLONE_DIR ?= $(CURRENT_MAKEFILE_DIR)/openapi-generator

OPENAPI_PROJECT_NAME ?= GravatarOpenAPIClient
OPENAPI_DIR ?= $(CURRENT_MAKEFILE_DIR)/$(OPENAPI_REL_DIR)
OPENAPI_REL_DIR ?= openapi
OPENAPI_GENERATED_DIR ?= $(CURRENT_MAKEFILE_DIR)/openapi/$(OPENAPI_PROJECT_NAME)
OPENAPI_CLIENT_PROPERTIES ?= projectName=$(OPENAPI_PROJECT_NAME),useSPMFileStructure=true

OPENAPI_YAML_PATH ?= $(CURRENT_MAKEFILE_DIR)/openapi/spec.yaml
MODEL_TEMPLATE_PATH ?= $(CURRENT_MAKEFILE_DIR)/openapi
OUTPUT_DIRECTORY ?= $(CURRENT_MAKEFILE_DIR)/Sources/Gravatar/OpenApi/Generated

# Derived values (don't change these).
CURRENT_MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
Expand Down Expand Up @@ -94,7 +100,7 @@ validate-pod: bundle-install
xcrun simctl list >> /dev/null
bundle exec pod lib lint \
--include-podspecs="*.podspec" \
--verbose --fail-fast
--verbose --fail-fast --allow-warnings

update-example-snapshots:
for filePath in ./Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples/*; \
Expand All @@ -104,24 +110,19 @@ update-example-snapshots:
cd ./Sources/GravatarUI/GravatarUI.docc/Resources/ProfileExamples && \
for filePath in *; do name=$${filePath%.*}; mv $$filePath $${name//-dark/~dark}@2x$${filePath#$$name}; done

install-and-generate: $(OPENAPI_GENERATOR_CLONE_DIR) # Clones and setup the openapi-generator.
"$(OPENAPI_GENERATOR_CLONE_DIR)"/run-in-docker.sh mvn package
make generate

generate: $(OUTPUT_DIRECTORY) # Generates the open-api model
cp "$(OPENAPI_YAML_PATH)" "$(OPENAPI_GENERATOR_CLONE_DIR)"/openapi.yaml
mkdir -p "$(OPENAPI_GENERATOR_CLONE_DIR)"/templates
cp "$(MODEL_TEMPLATE_PATH)"/*.mustache "$(OPENAPI_GENERATOR_CLONE_DIR)"/templates/
rm -rf "$(OPENAPI_GENERATOR_CLONE_DIR)"/generated/OpenAPIClient/Classes/OpenAPIs/Models/*
"$(OPENAPI_GENERATOR_CLONE_DIR)"/run-in-docker.sh generate -i openapi.yaml \
--global-property models \
-t templates \
-g swift5 \
-o ./generated \
-p packageName=Gravatar \
--additional-properties=useJsonEncodable=false,readonlyProperties=true && \
rsync -av --delete "$(OPENAPI_GENERATOR_CLONE_DIR)"/generated/OpenAPIClient/Classes/OpenAPIs/Models/ "$(OUTPUT_DIRECTORY)/" && \
make swiftformat && \
generate: $(OPENAPI_GENERATED_DIR) # Generates the open-api model
rm -rf "$(OPENAPI_GENERATED_DIR)/*" && \
docker run --rm \
-v $(OPENAPI_DIR):/local openapitools/openapi-generator-cli:"$(OPENAPI_GENERATOR_GIT_TAG)" generate \
-i /local/openapi.yaml \
-o /local/GravatarOpenAPIClient \
-t /local/templates \
-g swift5 \
-p packageName=Gravatar \
--additional-properties=useJsonEncodable=false,readonlyProperties=true,$(OPENAPI_CLIENT_PROPERTIES) && \
rsync -av --delete "$(OPENAPI_GENERATED_DIR)/Sources/$(OPENAPI_PROJECT_NAME)/" "$(CURRENT_MAKEFILE_DIR)/Sources/$(OPENAPI_PROJECT_NAME)/" && \
cp -fp "$(OPENAPI_GENERATED_DIR)/$(OPENAPI_PROJECT_NAME).podspec" "$(CURRENT_MAKEFILE_DIR)/" && \
make swiftformat && \
echo "DONE! 🎉"

generate-strings: bundle-install
Expand All @@ -131,22 +132,22 @@ download-strings: bundle-install
bundle exec fastlane download_localized_strings

clean-generated: # Delete the output directory used for generated sources.
@echo 'Delete entire directory: $(OUTPUT_DIRECTORY)? [y/N] ' && read ans && [ $${ans:-N} = y ] || (echo "Aborted"; exit 1)
rm -rf "$(OUTPUT_DIRECTORY)"
@echo 'Delete entire directory: $(OPENAPI_GENERATED_DIR)? [y/N] ' && read ans && [ $${ans:-N} = y ] || (echo "Aborted"; exit 1)
rm -rf "$(OPENAPI_GENERATED_DIR)"

clean: # Clean everything, including the checkout of swift-openapi-generator.
clean: # Clean everything
@echo 'Delete checkout of openapi-generator $(OPENAPI_GENERATOR_CLONE_DIR)? [y/N] ' && read ans && [ $${ans:-N} = y ] || (echo "Aborted"; exit 1)
rm -rf "$(OPENAPI_GENERATOR_CLONE_DIR)"
rm -rf "$(OPENAPI_GENERATOR_CLONE_DIR)/*"


dump: # Dump all derived values used by the Makefile.
@echo "CURRENT_MAKEFILE_PATH = $(CURRENT_MAKEFILE_PATH)"
@echo "CURRENT_MAKEFILE_DIR = $(CURRENT_MAKEFILE_DIR)"
@echo "OPENAPI_GENERATED_DIR = $(OPENAPI_GENERATED_DIR)"
@echo "OPENAPI_GENERATOR_GIT_URL = $(OPENAPI_GENERATOR_GIT_URL)"
@echo "OPENAPI_GENERATOR_GIT_TAG = $(OPENAPI_GENERATOR_GIT_TAG)"
@echo "OPENAPI_GENERATOR_CLONE_DIR = $(OPENAPI_GENERATOR_CLONE_DIR)"
@echo "OPENAPI_YAML_PATH = $(OPENAPI_YAML_PATH)"
@echo "OUTPUT_DIRECTORY = $(OUTPUT_DIRECTORY)"

$(OPENAPI_GENERATOR_CLONE_DIR):
git \
Expand All @@ -157,5 +158,5 @@ $(OPENAPI_GENERATOR_CLONE_DIR):
"$(OPENAPI_GENERATOR_GIT_URL)" \
$@

$(OUTPUT_DIRECTORY):
$(OPENAPI_GENERATED_DIR):
mkdir -p "$@"
2 changes: 1 addition & 1 deletion Package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "2d82ed06a27431c1da79790f8b215b8abf6d2a7397f42f02e364c7a92f86a5ab",
"originHash" : "ce90f93a035f7d411d81060dce4cdc6d5bcbe81fce7d7260084532fa492ff797",
"pins" : [
{
"identity" : "swift-snapshot-testing",
Expand Down
9 changes: 8 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.54.0"),
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.8.1"),
.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", exact: "0.56.2"),
.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", exact: "0.56.2")
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
.target(
name: "Gravatar",
dependencies: ["GravatarOpenAPIClient"],
resources: [.process("Resources")],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency")
Expand Down Expand Up @@ -72,6 +73,12 @@ let package = Package(
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency")
]
),
.target(
name: "GravatarOpenAPIClient",
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency")
]
)
]
)
23 changes: 23 additions & 0 deletions Sources/Gravatar/OpenAPIExports.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import GravatarOpenAPIClient

// MARK: OpenAPI Models: Public

public typealias CryptoWalletAddress = GravatarOpenAPIClient.CryptoWalletAddress
public typealias GalleryImage = GravatarOpenAPIClient.GalleryImage
public typealias Interest = GravatarOpenAPIClient.Interest
public typealias Language = GravatarOpenAPIClient.Language
public typealias Link = GravatarOpenAPIClient.Link
public typealias Profile = GravatarOpenAPIClient.Profile
public typealias ProfileContactInfo = GravatarOpenAPIClient.ProfileContactInfo
public typealias ProfilePayments = GravatarOpenAPIClient.ProfilePayments
public typealias VerifiedAccount = GravatarOpenAPIClient.VerifiedAccount

// MARK: - OpenAPI Models: Package

package typealias Avatar = GravatarOpenAPIClient.Avatar

// MARK: - OpenAPI Models: Internal

typealias AssociatedResponse = GravatarOpenAPIClient.AssociatedResponse
typealias ModelError = GravatarOpenAPIClient.ModelError
typealias SetEmailAvatarRequest = GravatarOpenAPIClient.SetEmailAvatarRequest
25 changes: 0 additions & 25 deletions Sources/Gravatar/OpenApi/Generated/SetEmailAvatarRequest.swift

This file was deleted.

111 changes: 111 additions & 0 deletions Sources/GravatarOpenAPIClient/APIHelper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import Foundation

public enum APIHelper {
public static func rejectNil(_ source: [String: Any?]) -> [String: Any]? {
let destination = source.reduce(into: [String: Any]()) { result, item in
if let value = item.value {
result[item.key] = value
}
}

if destination.isEmpty {
return nil
}
return destination
}

public static func rejectNilHeaders(_ source: [String: Any?]) -> [String: String] {
source.reduce(into: [String: String]()) { result, item in
if let collection = item.value as? [Any?] {
result[item.key] = collection
.compactMap { value in convertAnyToString(value) }
.joined(separator: ",")
} else if let value: Any = item.value {
result[item.key] = convertAnyToString(value)
}
}
}

public static func convertBoolToString(_ source: [String: Any]?) -> [String: Any]? {
guard let source else {
return nil
}

return source.reduce(into: [String: Any]()) { result, item in
switch item.value {
case let x as Bool:
result[item.key] = x.description
default:
result[item.key] = item.value
}
}
}

public static func convertAnyToString(_ value: Any?) -> String? {
guard let value else { return nil }
if let value = value as? any RawRepresentable {
return "\(value.rawValue)"
} else {
return "\(value)"
}
}

public static func mapValueToPathItem(_ source: Any) -> Any {
if let collection = source as? [Any?] {
return collection
.compactMap { value in convertAnyToString(value) }
.joined(separator: ",")
}
return source
}

/// maps all values from source to query parameters
///
/// explode attribute is respected: collection values might be either joined or split up into separate key value pairs
public static func mapValuesToQueryItems(_ source: [String: (wrappedValue: Any?, isExplode: Bool)]) -> [URLQueryItem]? {
let destination = source.filter { $0.value.wrappedValue != nil }.reduce(into: [URLQueryItem]()) { result, item in
if let collection = item.value.wrappedValue as? [Any?] {
let collectionValues: [String] = collection.compactMap { value in convertAnyToString(value) }

if !item.value.isExplode {
result.append(URLQueryItem(name: item.key, value: collectionValues.joined(separator: ",")))
} else {
for value in collectionValues {
result.append(URLQueryItem(name: item.key, value: value))
}
}

} else if let value = item.value.wrappedValue {
result.append(URLQueryItem(name: item.key, value: convertAnyToString(value)))
}
}

if destination.isEmpty {
return nil
}
return destination.sorted { $0.name < $1.name }
}

/// maps all values from source to query parameters
///
/// collection values are always exploded
public static func mapValuesToQueryItems(_ source: [String: Any?]) -> [URLQueryItem]? {
let destination = source.filter { $0.value != nil }.reduce(into: [URLQueryItem]()) { result, item in
if let collection = item.value as? [Any?] {
collection
.compactMap { value in convertAnyToString(value) }
.forEach { value in
result.append(URLQueryItem(name: item.key, value: value))
}

} else if let value = item.value {
result.append(URLQueryItem(name: item.key, value: convertAnyToString(value)))
}
}

if destination.isEmpty {
return nil
}
return destination.sorted { $0.name < $1.name }
}
}
Loading