Skip to content

Commit

Permalink
feat: initial move from microsoft/kiota
Browse files Browse the repository at this point in the history
  • Loading branch information
baywet committed Jul 23, 2024
1 parent 4a82912 commit 30705f2
Show file tree
Hide file tree
Showing 27 changed files with 459 additions and 0 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/abstractions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Swift abstractions

on:
workflow_dispatch:
push:
branches: [ main ]
paths: ['abstractions/**', '.github/workflows/**']
pull_request:
paths: ['abstractions/**', '.github/workflows/**']

jobs:
build:
runs-on: ubuntu-latest
env:
relativePath: ./abstractions
steps:
- uses: actions/checkout@v4
- uses: swift-actions/setup-swift@v2
with:
swift-version: '5.7'
- name: Build SDK project
run: swift build
working-directory: ${{ env.relativePath }}
- name: Run unit tests
run: swift test
working-directory: ${{ env.relativePath }}
9 changes: 9 additions & 0 deletions abstractions/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
20 changes: 20 additions & 0 deletions abstractions/MicrosoftKiotaAbstractions.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Pod::Spec.new do |s|
s.name = "MicrosoftKiotaAbstractions"
s.version = "1.0.0"
s.summary = "MicrosoftKiotaAbstractions provides the base infrastructure for the Kiota-generated SDKs to function.
It defines multiple concepts related to abstract HTTP requests, serialization, and authentication.
These concepts can then be implemented independently without tying the SDKs to any specific implementation.
Kiota also provides default implementations for these concepts."
s.homepage = "https://github.com/microsoft/kiota"
s.license = { :type => "MIT" }
s.authors = { "Microsoft" => "[email protected]" }

s.requires_arc = true
s.swift_version = "5.0"
s.osx.deployment_target = "10.9"
s.ios.deployment_target = "9.0"
s.watchos.deployment_target = "3.0"
s.tvos.deployment_target = "9.0"
s.source = { :git => "https://github.com/microsoft/kiota.git", :tag => s.version }
s.source_files = "Source/*.swift"
end

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

34 changes: 34 additions & 0 deletions abstractions/Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"object": {
"pins": [
{
"package": "PathKit",
"repositoryURL": "https://github.com/kylef/PathKit.git",
"state": {
"branch": null,
"revision": "3bfd2737b700b9a36565a8c94f4ad2b050a5e574",
"version": "1.0.1"
}
},
{
"package": "Spectre",
"repositoryURL": "https://github.com/kylef/Spectre.git",
"state": {
"branch": null,
"revision": "26cc5e9ae0947092c7139ef7ba612e34646086c7",
"version": "0.10.1"
}
},
{
"package": "URITemplate",
"repositoryURL": "https://github.com/kylef/URITemplate.swift.git",
"state": {
"branch": null,
"revision": "a309673fdf86e4919a0250730e461ac533a03b3a",
"version": "3.0.1"
}
}
]
},
"version": 1
}
17 changes: 17 additions & 0 deletions abstractions/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// swift-tools-version:5.0
import PackageDescription

let package = Package(
name: "MicrosoftKiotaAbstractions",
products: [
.library(name: "MicrosoftKiotaAbstractions", targets: ["MicrosoftKiotaAbstractions"])
],
dependencies: [
.package(url: "https://github.com/kylef/URITemplate.swift.git", from: "3.0.0")
],
targets: [
.target(name: "MicrosoftKiotaAbstractions", dependencies: ["URITemplate"]),
.testTarget(name: "MicrosoftKiotaAbstractionsTests", dependencies: ["MicrosoftKiotaAbstractions"])
],
swiftLanguageVersions: [.v5]
)
23 changes: 23 additions & 0 deletions abstractions/Podfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'

target 'MicrosoftKiotaAbstractions' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!

# Pods for MicrosoftKiotaAbstractions
pod 'URITemplate'

target 'MicrosoftKiotaAbstractionsTests' do
# Pods for testing
end

end

target 'MicrosoftKiotaAbstractionsPackageDescription' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!

# Pods for MicrosoftKiotaAbstractionsPackageDescription

end
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
public class ApiClientBuilder {
private init() {

}
private static let defaultSerializationWriterFactoryInstanceIntl = SerializationWriterFactoryRegistry()
public static var defaultSerializationWriterFactoryInstance: SerializationWriterFactory {
get {
return defaultSerializationWriterFactoryInstanceIntl
}
}
public static func registerDefaultSerializer(metaFactory: () -> SerializationWriterFactory) {
let factory = metaFactory()
if let contentType = try? factory.getValidContentType() {
if contentType != "" {
defaultSerializationWriterFactoryInstanceIntl.contentTypeAssociatedFactories[contentType] = factory
}
}
}
private static let defaultParseNodeFactoryInstanceIntl = ParseNodeFactoryRegistry()
public static var defaultParseNodeFactoryInstance: ParseNodeFactory {
get {
return defaultParseNodeFactoryInstanceIntl
}
}
public static func registerDefaultParser(metaFactory: () -> ParseNodeFactory) {
let factory = metaFactory()
if let contentType = try? factory.getValidContentType() {
if contentType != "" {
defaultParseNodeFactoryInstanceIntl.contentTypeAssociatedFactories[contentType] = factory
}
}
}
}
3 changes: 3 additions & 0 deletions abstractions/Source/MicrosoftKiotaAbstractions/ApiError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public enum ApiError: Error {
case unknownError(String, Any? = nil)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Foundation

public protocol AccessTokenProvider {
func getAuthenticationToken(url: URL) async throws -> String?
var allowedHostsValidator: AllowedHostsValidator { get }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import Foundation
public class AllowedHostsValidator {
private var validHostsIntl = Set<String>()
public init(validHosts: [String]) {
self.validHosts = validHosts;
}
public var validHosts: [String] { get {
return Array(validHostsIntl)

} set (validHosts) {
self.validHostsIntl = Set(validHosts.map { $0.lowercased() })
}}
public func isUrlHostValid(url: URL) -> Bool {
if let host = url.host {
return validHostsIntl.contains(host.lowercased())
}
return false
}
}
public let isSchemeHttps = { (url: URL) -> Bool in
if let scheme = url.scheme {
return scheme.lowercased() == "https"
}
return false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public class AnonymousAuthenticationProvider : AuthenticationProvider {
public func authenticateRequest(request: RequestInformation) async throws {
// Do nothing
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol AuthenticationProvider {
func authenticateRequest(request: RequestInformation) async throws
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
public class BaseBearerAuthenticationProvider : AuthenticationProvider {
public init (accessTokenProvider: AccessTokenProvider) {
accessTokenProviderIntl = accessTokenProvider
}
private var accessTokenProviderIntl: AccessTokenProvider
public var accessTokenProvider: AccessTokenProvider {
get {
return accessTokenProviderIntl
}
}
public let authorizationHeaderKey = "Authorization"
public let authorizationHeaderValuePrefix = "Bearer "
public func authenticateRequest(request: RequestInformation) async throws {
if request.headers[authorizationHeaderKey] == nil {
let url = try request.getUri()
let tokenResult = try? await accessTokenProvider.getAuthenticationToken(url: url)
if let token = tokenResult {
request.headers[authorizationHeaderKey] = authorizationHeaderValuePrefix + token
}
}
}
}
11 changes: 11 additions & 0 deletions abstractions/Source/MicrosoftKiotaAbstractions/HttpMethod.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
public enum HttpMethod {
case get
case post
case patch
case delete
case options
case connect
case put
case trace
case head
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
public typealias ErrorMappings = [String:ParsableFactory]
public typealias ResponseHandler<DeserializationType> = (Any, ErrorMappings) async throws -> DeserializationType
public protocol RequestAdapter {
func send<T:Parsable>(request: RequestInformation, ctor: ParsableFactory, responseHandler: ResponseHandler<T>?, errorMappings: ErrorMappings?) async throws -> T?
func sendCollection<T:Parsable>(request: RequestInformation, ctor: ParsableFactory, responseHandler: ResponseHandler<T>?, errorMappings: ErrorMappings?) async throws -> [T]?
func sendPrimitive<T>(request: RequestInformation, responseHandler: ResponseHandler<T>?, errorMappings: ErrorMappings?) async throws -> T?
func sendNoContent(request: RequestInformation, responseHandler: ResponseHandler<Void>?, errorMappings: ErrorMappings?) async throws -> Void
func enableBackingStore() throws
var baseUrl: String { get set }
var serializationWriterFactory: SerializationWriterFactory { get }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import Foundation
import URITemplate

public enum RequestInformationErrors : Error {
case emptyUrlTemplate
case emptyContentType
case unableToExpandUriTemplate
case invalidRawUrl
}

public class RequestInformation {
public var method: HttpMethod = HttpMethod.get
public var urlTemplate = ""
public var queryParameters = [String:String]()
public var pathParameters = [String:String]()
public var headers = [String:String]()
var contentInternal: Data?
public var content: Data? {
get {
return contentInternal
}
set(newContent) {
contentInternal = newContent
if newContent != nil {
headers[contentTypeHeaderKey] = binaryContentType
}
}
}
var uriInternal: URL?
public func getUri() throws -> URL {
if let uriInternalValue = uriInternal {
return uriInternalValue
}
guard !urlTemplate.isEmpty else {
throw RequestInformationErrors.emptyUrlTemplate
}
if let rawUrl = pathParameters[rawUrlKey] {
if rawUrl != "" {
let newValue = URL(string: rawUrl)
if let url = newValue {
setUri(newUri: url)
return url
}
}
throw RequestInformationErrors.invalidRawUrl
} else {
let urlTemplate = URITemplate(template: self.urlTemplate)
let merged = pathParameters.merging(queryParameters)
{ (first, _) in first }
let url = urlTemplate.expand(merged)
if let newValue = URL(string: url) {
return newValue
} else {
throw RequestInformationErrors.unableToExpandUriTemplate
}
}
}
public func setUri(newUri: URL) {
uriInternal = newUri
pathParameters.removeAll()
queryParameters.removeAll()
}
var options = [String:RequestOption]()
let rawUrlKey = "request-raw-url"
let contentTypeHeaderKey = "Content-Type"
let binaryContentType = "application/octet-stream"
public func addRequestOption(options:RequestOption...) {
for option in options {
self.options[option.key] = option
}
}
public func getRequestOptions() -> [RequestOption] {
return [RequestOption](options.values)
}
public func setContentFromParsable<T: Parsable>(requestAdapter: RequestAdapter, contentType: String, item: T) throws {
guard contentType != "" else {
throw RequestInformationErrors.emptyContentType
}
if let writer = try? requestAdapter.serializationWriterFactory.getSerializationWriter(contentType: contentType) {
try writer.writeObjectValue(key: "", value: item)
self.content = try? writer.getSerializedContent()
self.headers[contentTypeHeaderKey] = contentType
}
}
public func setContentFromParsableCollection<T: Parsable>(requestAdapter: RequestAdapter, contentType: String, items: [T]) throws {
guard contentType != "" else {
throw RequestInformationErrors.emptyContentType
}
if let writer = try? requestAdapter.serializationWriterFactory.getSerializationWriter(contentType: contentType) {
try writer.writeCollectionOfObjectValues(key: "", value: items)
self.content = try? writer.getSerializedContent()
self.headers[contentTypeHeaderKey] = contentType
}
}
//TODO add query parameters from object by reflection
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol RequestOption {
var key: String { get }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
public protocol AdditionalDataHolder {
var additionalData : [String:Any] { get set }
}
Loading

0 comments on commit 30705f2

Please sign in to comment.