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
14 changes: 12 additions & 2 deletions Sources/FigmaAPI/FigmaClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ import Foundation
import FoundationNetworking
#endif

public enum FigmaAuth {
case personalToken(String)
case oauth(String)
}

final public class FigmaClient: BaseClient {

private let baseURL = URL(string: "https://api.figma.com/v1/")!
private let retryConfiguration: RetryConfiguration

public init(
accessToken: String,
auth: FigmaAuth,
timeout: TimeInterval?,
retryConfiguration: RetryConfiguration = .default,
requestDelay: TimeInterval? = nil
Expand All @@ -30,7 +35,12 @@ final public class FigmaClient: BaseClient {
self.retryConfiguration = retryConfiguration
}
let config = URLSessionConfiguration.ephemeral
config.httpAdditionalHeaders = ["X-Figma-Token": accessToken]
switch auth {
case .personalToken(let token):
config.httpAdditionalHeaders = ["X-Figma-Token": token]
case .oauth(let token):
config.httpAdditionalHeaders = ["Authorization": "Bearer \(token)"]
}
config.timeoutIntervalForRequest = timeout ?? 30
super.init(baseURL: baseURL, config: config)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/FigmaExport/FigmaExportCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ enum FigmaExportError: LocalizedError {
case .componentsNotFound:
return "Components not found in the Figma file. Have you published Components to the Library?"
case .accessTokenNotFound:
return "Environment variable FIGMA_PERSONAL_TOKEN not specified."
return "No Figma token found. Set FIGMA_OAUTH_TOKEN or FIGMA_PERSONAL_TOKEN."
case .colorsAssetsFolderNotSpecified:
return "Option ios.colors.assetsFolder not specified in configuration file."
case .custom(let errorString):
Expand Down
28 changes: 19 additions & 9 deletions Sources/FigmaExport/Input/FigmaExportOptions.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import ArgumentParser
import FigmaAPI
import Foundation
import Yams

Expand All @@ -8,24 +9,33 @@ struct FigmaExportOptions: ParsableArguments {
@Option(name: .shortAndLong, help: "An input YAML file with figma and platform properties.")
var input: String = Self.input

var accessToken: String!
// Set during validate(), excluded from Decodable via CodingKeys
private(set) var auth: FigmaAuth!
private(set) var params: Params!

var params: Params!
private enum CodingKeys: String, CodingKey {
case input
}

mutating func validate() throws {
guard let token = ProcessInfo.processInfo.environment["FIGMA_PERSONAL_TOKEN"] else {
throw FigmaExportError.accessTokenNotFound
auth = try Self.resolveAuth()
params = try readParams(at: input)
}

private static func resolveAuth() throws -> FigmaAuth {
let env = ProcessInfo.processInfo.environment
if let token = env["FIGMA_OAUTH_TOKEN"] {
return .oauth(token)
} else if let token = env["FIGMA_PERSONAL_TOKEN"] {
return .personalToken(token)
}
self.accessToken = token
self.params = try readParams(at: input)
throw FigmaExportError.accessTokenNotFound
}

private func readParams(at path: String) throws -> Params {
let url = URL(fileURLWithPath: path)
let data = try Data(contentsOf: url)
let string = String(decoding: data, as: UTF8.self)
let decoder = YAMLDecoder()
return try decoder.decode(Params.self, from: string)
return try YAMLDecoder().decode(Params.self, from: string)
}
}

2 changes: 1 addition & 1 deletion Sources/FigmaExport/Subcommands/ExportColors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extension FigmaExportCommand {
logger.info("Using FigmaExport \(FigmaExportCommand.version) to export colors.")
logger.info("Fetching colors. Please wait...")

let client = FigmaClient(accessToken: options.accessToken, timeout: options.params.figma.timeout, requestDelay: options.params.figma.requestDelay)
let client = FigmaClient(auth: options.auth, timeout: options.params.figma.timeout, requestDelay: options.params.figma.requestDelay)
let commonParams = options.params.common

if commonParams?.colors != nil, commonParams?.variablesColors != nil {
Expand Down
2 changes: 1 addition & 1 deletion Sources/FigmaExport/Subcommands/ExportIcons.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extension FigmaExportCommand {
var filter: String?

func run() throws {
let client = FigmaClient(accessToken: options.accessToken, timeout: options.params.figma.timeout, requestDelay: options.params.figma.requestDelay)
let client = FigmaClient(auth: options.auth, timeout: options.params.figma.timeout, requestDelay: options.params.figma.requestDelay)

if options.params.ios != nil {
logger.info("Using FigmaExport \(FigmaExportCommand.version) to export icons to Xcode project.")
Expand Down
2 changes: 1 addition & 1 deletion Sources/FigmaExport/Subcommands/ExportImages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extension FigmaExportCommand {
var filter: String?

func run() throws {
let client = FigmaClient(accessToken: options.accessToken, timeout: options.params.figma.timeout, requestDelay: options.params.figma.requestDelay)
let client = FigmaClient(auth: options.auth, timeout: options.params.figma.timeout, requestDelay: options.params.figma.requestDelay)

if let _ = options.params.ios {
logger.info("Using FigmaExport \(FigmaExportCommand.version) to export images to Xcode project.")
Expand Down
2 changes: 1 addition & 1 deletion Sources/FigmaExport/Subcommands/ExportTypography.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ extension FigmaExportCommand {
var options: FigmaExportOptions

func run() throws {
let client = FigmaClient(accessToken: options.accessToken, timeout: options.params.figma.timeout, requestDelay: options.params.figma.requestDelay)
let client = FigmaClient(auth: options.auth, timeout: options.params.figma.timeout, requestDelay: options.params.figma.requestDelay)

logger.info("Using FigmaExport \(FigmaExportCommand.version) to export typography.")

Expand Down
38 changes: 38 additions & 0 deletions Tests/FigmaAPITests/FigmaAuthTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import XCTest
import Foundation
@testable import FigmaAPI

final class FigmaAuthTests: XCTestCase {

func testPersonalTokenAssociatedValue() {
let auth = FigmaAuth.personalToken("test-pat-token")
if case .personalToken(let token) = auth {
XCTAssertEqual(token, "test-pat-token")
} else {
XCTFail("Expected personalToken case")
}
}

func testOAuthAssociatedValue() {
let auth = FigmaAuth.oauth("test-oauth-token")
if case .oauth(let token) = auth {
XCTAssertEqual(token, "test-oauth-token")
} else {
XCTFail("Expected oauth case")
}
}

func testPersonalTokenIsNotOAuth() {
let auth = FigmaAuth.personalToken("token")
if case .oauth = auth {
XCTFail("personalToken should not match oauth case")
}
}

func testOAuthIsNotPersonalToken() {
let auth = FigmaAuth.oauth("token")
if case .personalToken = auth {
XCTFail("oauth should not match personalToken case")
}
}
}