Add more shop request

This commit is contained in:
2023-05-19 01:17:09 +09:00
parent 6398a973d4
commit e01c539adc
8 changed files with 180 additions and 32 deletions

View File

@@ -30,7 +30,8 @@
341F5F072A14634F00962D48 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F062A14634F00962D48 /* Extensions.swift */; };
341F5F0B2A15115400962D48 /* KissShop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F0A2A15115400962D48 /* KissShop.swift */; };
341F5F0D2A15222E00962D48 /* AuthRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F0C2A15222E00962D48 /* AuthRequest.swift */; };
341F5F0F2A15223A00962D48 /* SeibroRequestRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F0E2A15223A00962D48 /* SeibroRequestRequest.swift */; };
341F5F0F2A15223A00962D48 /* SeibroRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F0E2A15223A00962D48 /* SeibroRequest.swift */; };
341F5F112A1685E700962D48 /* ShopRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 341F5F102A1685E700962D48 /* ShopRequest.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -68,7 +69,8 @@
341F5F062A14634F00962D48 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
341F5F0A2A15115400962D48 /* KissShop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KissShop.swift; sourceTree = "<group>"; };
341F5F0C2A15222E00962D48 /* AuthRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthRequest.swift; sourceTree = "<group>"; };
341F5F0E2A15223A00962D48 /* SeibroRequestRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeibroRequestRequest.swift; sourceTree = "<group>"; };
341F5F0E2A15223A00962D48 /* SeibroRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeibroRequest.swift; sourceTree = "<group>"; };
341F5F102A1685E700962D48 /* ShopRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShopRequest.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -170,7 +172,8 @@
341F5EDD2A0F300100962D48 /* Request.swift */,
341F5F0C2A15222E00962D48 /* AuthRequest.swift */,
341F5EFE2A10955D00962D48 /* OrderRequest.swift */,
341F5F0E2A15223A00962D48 /* SeibroRequestRequest.swift */,
341F5F0E2A15223A00962D48 /* SeibroRequest.swift */,
341F5F102A1685E700962D48 /* ShopRequest.swift */,
341F5F022A11A2BC00962D48 /* Credential.swift */,
341F5F062A14634F00962D48 /* Extensions.swift */,
);
@@ -294,7 +297,7 @@
341F5EFD2A10931B00962D48 /* DomesticStockSearch.swift in Sources */,
341F5EE52A0F3EF400962D48 /* DomesticStock.swift in Sources */,
341F5EF72A0F8B0500962D48 /* DomesticStockResult.swift in Sources */,
341F5F0F2A15223A00962D48 /* SeibroRequestRequest.swift in Sources */,
341F5F0F2A15223A00962D48 /* SeibroRequest.swift in Sources */,
341F5EF02A0F886600962D48 /* ForeignFutures.swift in Sources */,
341F5EEC2A0F883900962D48 /* ForeignStock.swift in Sources */,
341F5EFF2A10955D00962D48 /* OrderRequest.swift in Sources */,
@@ -303,6 +306,7 @@
341F5EDE2A0F300100962D48 /* Request.swift in Sources */,
341F5F012A11155100962D48 /* DomesticStockSearchResult.swift in Sources */,
341F5EF22A0F887200962D48 /* DomesticFutures.swift in Sources */,
341F5F112A1685E700962D48 /* ShopRequest.swift in Sources */,
341F5EF92A0F907300962D48 /* DomesticStockPriceResult.swift in Sources */,
341F5EE12A0F373B00962D48 /* Login.swift in Sources */,
341F5EF52A0F891200962D48 /* KissAccount.swift in Sources */,

View File

@@ -53,9 +53,7 @@ public struct KissCredential: Credential, Codable {
public init(isMock: Bool) throws {
let serverFileName = isMock ? "mock-server.json": "real-server.json"
let path = "\(FileManager.default.currentDirectoryPath)/\(serverFileName)"
let jsonUrl = URL(filePath: path)
let jsonUrl = URL.currentDirectory().appending(path: serverFileName)
try self.init(jsonUrl: jsonUrl)
}
}

View File

@@ -32,6 +32,7 @@ public enum GeneralError: Error {
public enum QueryError: Error {
case invalidUrl
case invalidJson
case invalidXml
case missingData
}

View File

@@ -13,6 +13,7 @@ protocol SeibroRequest: Request {
}
/// _
extension SeibroRequest {
public var domain: String {
@@ -27,7 +28,6 @@ extension SeibroRequest {
URL(string: domain + url)
}
/*
func query(completion: @escaping (Result<KResult, Error>) -> Void) {
guard let url = queryUrl else {
completion(.failure(QueryError.invalidUrl))
@@ -86,16 +86,26 @@ extension SeibroRequest {
let stringData = String(data: data, encoding: .utf8) ?? ""
print(stringData)
do {
let parser = XMLParser()
parser.delegate =
let result = try decoder.decode(KResult.self, from: data)
completion(.success(result))
} catch {
completion(.failure(error))
let parser = XMLParser(data: data)
let handler = SeibroResultParser<KResult>()
parser.delegate = handler
if parser.parse() {
// completion(.success(""))
} else {
completion(.failure(QueryError.invalidXml))
}
}.resume()
}
*/
}
class SeibroResultParser<T>: NSObject, XMLParserDelegate {
let data: T? = nil
func parserDidStartDocument(_ parser: XMLParser) {
}
func parserDidEndDocument(_ parser: XMLParser) {
}
}

View File

@@ -0,0 +1,30 @@
//
// ShopRequest.swift
// KissMe
//
// Created by ened-book-m1 on 2023/05/19.
//
import Foundation
protocol ShopRequest: AuthRequest {
var openApiKey: String { get }
}
/// _KRX
extension ShopRequest {
public var domain: String {
"https://apis.data.go.kr"
}
public var timeout: TimeInterval {
15
}
var queryUrl: URL? {
URL(string: domain + url)
}
}

View File

@@ -9,47 +9,101 @@ import Foundation
public class KissShop {
struct Key: Codable {
struct ShopCredential: Codable {
let openApiKey: String
init(openApiKey: String) {
self.openApiKey = openApiKey
}
init(jsonUrl: URL) throws {
do {
let data = try Data(contentsOf: jsonUrl, options: .uncached)
let jsonData = try JSONDecoder().decode(ShopCredential.self, from: data)
self.init(openApiKey: jsonData.openApiKey)
} catch {
throw error
}
}
}
init(jsonUrl: URL) {
//self.openApiKey = openApiKey
let credential: ShopCredential
public init(jsonUrl: URL) throws {
credential = try ShopCredential(jsonUrl: jsonUrl)
}
}
extension KissShop {
public struct DomesticShop {
}
extension DomesticShop {
public enum MarketCode: Int {
///
case stock = 11
///
case kosdaq = 12
/// K-OTC
case kotc = 13
///
case konex = 14
///
case etc = 50
}
public struct ProductResult: Codable {
// TODO: work
}
/// _ -
public struct ProductRequest: SeibroRequest {
public typealias KResult = String
public var url: String {
"/openapi/service/StockSvc/getKDRSecnInfo"
"/openapi/service/StockSvc/getShotnByMartN1"
}
public var method: Method { .get }
public var body: [String : Any] {
[
"ServiceKey": openApiKey,
"caltotMartTpcd": String(market.rawValue),
"martTpcd": String(market.rawValue),
"pageNo": 1,
"numOfRows": 10000,
]
}
public var result: KResult? = nil
public enum MarketCode: Int {
case stock = 11
case kosdaq = 12
case konex = 13
}
public let openApiKey: String
let market: MarketCode
init(openApiKey: String, market: MarketCode) {
self.openApiKey = openApiKey
self.market = market
}
}
}
extension KissShop {
public func getProduct(market: DomesticShop.MarketCode) async throws -> String {
return try await withUnsafeThrowingContinuation { continuation in
let request = DomesticShop.ProductRequest(openApiKey: self.credential.openApiKey, market: market)
request.query { result in
switch result {
case .success(let result):
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
}

View File

@@ -12,6 +12,7 @@ import KissMe
class KissConsole {
var credential: Credential? = nil
var account: KissAccount? = nil
var shop: KissShop? = nil
enum KissCommand: String {
case quit = "quit"
@@ -21,13 +22,16 @@ class KissConsole {
case search = "search"
case buy = "buy"
case sell = "sell"
case loadShop = "load shop"
var needLogin: Bool {
switch self {
case .quit:
case .quit, .loginMock, .loginReal:
return false
case .loginMock, .loginReal, .logout, .search, .buy, .sell:
case .logout, .search, .buy, .sell:
return true
case .loadShop:
return false
}
}
}
@@ -36,6 +40,10 @@ class KissConsole {
account != nil
}
init() {
let jsonUrl = URL.currentDirectory().appending(path: "shop-server.json")
shop = try? KissShop(jsonUrl: jsonUrl)
}
func onLogin(isMock: Bool) async {
guard !isLogined else {
@@ -85,6 +93,15 @@ class KissConsole {
}
func onLoadShop() async {
do {
_ = try await shop?.getProduct(market: .kosdaq)
} catch {
print("\(error)")
}
}
func run() {
print("Enter command:")
let semaphore = DispatchSemaphore(value: 0)
@@ -114,6 +131,7 @@ class KissConsole {
case .search: await onSearch(args.suffixStrings(from: 1))
case .buy: await onBuy(args.suffixStrings(from: 1))
case .sell: await onSell(args.suffixStrings(from: 1))
case .loadShop: await onLoadShop()
default:
print("Unknown command: \(single)")
}

View File

@@ -60,3 +60,36 @@ func test_json_result() {
print("\(error)")
}
}
func test_xml_result() {
let str = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><response><header><resultCode>00</resultCode><resultMsg>NORMAL SERVICE.</resultMsg></header><body><items><item><korSecnNm>이스트아시아홀딩스인베스트먼트리미티드</korSecnNm><shotnIsin>900110</shotnIsin></item><item><korSecnNm>삼천당제약</korSecnNm><shotnIsin>000250</shotnIsin></item><item><korSecnNm>중앙에너비스</korSecnNm><shotnIsin>000440</shotnIsin></item><item><korSecnNm>신라섬유</korSecnNm><shotnIsin>001000</shotnIsin></item><item><korSecnNm>안국약품</korSecnNm><shotnIsin>001540</shotnIsin></item><item><korSecnNm>무림에스피</korSecnNm><shotnIsin>001810</shotnIsin></item><item><korSecnNm>이화공영</korSecnNm><shotnIsin>001840</shotnIsin></item><item><korSecnNm>피에스텍</korSecnNm><shotnIsin>002230</shotnIsin></item><item><korSecnNm>삼일기업공사</korSecnNm><shotnIsin>002290</shotnIsin></item><item><korSecnNm>한일사료</korSecnNm><shotnIsin>005860</shotnIsin></item></items><numOfRows>10</numOfRows><pageNo>1</pageNo><totalCount>1637</totalCount></body></response>"
let data = Data(str.utf8)
class ResultHelper: NSObject, XMLParserDelegate {
struct Result: Codable {
}
var result: Result?
func parserDidStartDocument(_ parser: XMLParser) {
}
func parserDidEndDocument(_ parser: XMLParser) {
}
}
do {
let helper = ResultHelper()
let parser = XMLParser(data: data)
parser.shouldProcessNamespaces = true
parser.delegate = helper
if parser.parse() {
print(helper.result)
}
}
}