Skip to content

Commit

Permalink
Adding support for Bulk endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
nsriva committed Sep 26, 2022
1 parent 6495bf2 commit bb1cd84
Show file tree
Hide file tree
Showing 8 changed files with 388 additions and 2 deletions.
20 changes: 20 additions & 0 deletions QuizTrain.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@

/* Begin PBXBuildFile section */
390B6DDC28B8159C00EEED63 /* QuizTrain.h in Headers */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* QuizTrain.h */; settings = {ATTRIBUTES = (Public, ); }; };
E0E0983828E215C8000432DC /* BulkCases.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0E0983728E215C8000432DC /* BulkCases.swift */; };
E0E0983A28E215EC000432DC /* BulkSections.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0E0983928E215EC000432DC /* BulkSections.swift */; };
E0E0983C28E2160C000432DC /* BulkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0E0983B28E2160C000432DC /* BulkTests.swift */; };
E0E0983E28E21640000432DC /* QuizTrainData.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0E0983D28E21640000432DC /* QuizTrainData.swift */; };
E0E0984028E2167D000432DC /* Links.swift in Sources */ = {isa = PBXBuildFile; fileRef = E0E0983F28E2167D000432DC /* Links.swift */; };
OBJ_251 /* AddRequestJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* AddRequestJSON.swift */; };
OBJ_252 /* AddRequestJSONKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* AddRequestJSONKeys.swift */; };
OBJ_253 /* CustomFieldsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* CustomFieldsContainer.swift */; };
Expand Down Expand Up @@ -221,6 +226,11 @@

/* Begin PBXFileReference section */
D088A3EA2681ACA7009FDB2A /* TestCredentials.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = TestCredentials.json; sourceTree = "<group>"; };
E0E0983728E215C8000432DC /* BulkCases.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BulkCases.swift; sourceTree = "<group>"; };
E0E0983928E215EC000432DC /* BulkSections.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BulkSections.swift; sourceTree = "<group>"; };
E0E0983B28E2160C000432DC /* BulkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BulkTests.swift; sourceTree = "<group>"; };
E0E0983D28E21640000432DC /* QuizTrainData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuizTrainData.swift; sourceTree = "<group>"; };
E0E0983F28E2167D000432DC /* Links.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Links.swift; sourceTree = "<group>"; };
OBJ_10 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
OBJ_100 /* NewCaseFieldConfigOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCaseFieldConfigOptions.swift; sourceTree = "<group>"; };
OBJ_101 /* NewCaseFieldData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCaseFieldData.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -882,6 +892,10 @@
OBJ_68 /* Test.swift */,
OBJ_69 /* Types */,
OBJ_72 /* User.swift */,
E0E0983728E215C8000432DC /* BulkCases.swift */,
E0E0983928E215EC000432DC /* BulkSections.swift */,
E0E0983B28E2160C000432DC /* BulkTests.swift */,
E0E0983D28E21640000432DC /* QuizTrainData.swift */,
);
path = Models;
sourceTree = "<group>";
Expand Down Expand Up @@ -915,6 +929,7 @@
children = (
OBJ_70 /* CustomFieldType.swift */,
OBJ_71 /* Project.SuiteMode.swift */,
E0E0983F28E2167D000432DC /* Links.swift */,
);
path = Types;
sourceTree = "<group>";
Expand Down Expand Up @@ -1140,6 +1155,7 @@
buildActionMask = 0;
files = (
OBJ_251 /* AddRequestJSON.swift in Sources */,
E0E0983A28E215EC000432DC /* BulkSections.swift in Sources */,
OBJ_252 /* AddRequestJSONKeys.swift in Sources */,
OBJ_253 /* CustomFieldsContainer.swift in Sources */,
OBJ_254 /* ErrorContainer.swift in Sources */,
Expand Down Expand Up @@ -1181,6 +1197,7 @@
OBJ_290 /* Status.swift in Sources */,
OBJ_291 /* Suite.swift in Sources */,
OBJ_292 /* Template.swift in Sources */,
E0E0983E28E21640000432DC /* QuizTrainData.swift in Sources */,
OBJ_293 /* Test.swift in Sources */,
OBJ_294 /* CustomFieldType.swift in Sources */,
OBJ_295 /* Project.SuiteMode.swift in Sources */,
Expand Down Expand Up @@ -1209,8 +1226,11 @@
OBJ_318 /* NewCaseFieldType.swift in Sources */,
OBJ_319 /* NewCaseResults.Result.swift in Sources */,
OBJ_320 /* NewCaseResults.swift in Sources */,
E0E0983C28E2160C000432DC /* BulkTests.swift in Sources */,
E0E0984028E2167D000432DC /* Links.swift in Sources */,
OBJ_321 /* NewConfiguration.swift in Sources */,
OBJ_322 /* NewConfigurationGroup.swift in Sources */,
E0E0983828E215C8000432DC /* BulkCases.swift in Sources */,
OBJ_323 /* NewMilestone.swift in Sources */,
OBJ_324 /* NewPlan.Entry.Run.swift in Sources */,
OBJ_325 /* NewPlan.Entry.swift in Sources */,
Expand Down
61 changes: 61 additions & 0 deletions Sources/QuizTrain/Models/BulkCases.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// BulkCases.swift
// QuizTrain
//
// Created by Neha Srivastava on 9/26/22.
//

public struct BulkCases {
public let offset: Int
public let limit: Int
public let size: Int
public let cases: [Case]
public let _links: Links
}

// MARK: - JSON Keys
extension BulkCases {

enum JSONKeys: JSONKey {
case offset = "offset"
case limit = "limit"
case size = "size"
case _links = "_links"
case cases = "cases"
case next = "next"
case prev = "prev"
}

}

// MARK: - Serialization
extension BulkCases: JSONDeserializable {

init?(json: JSONDictionary) {

let offset = json[JSONKeys.offset.rawValue] as! Int
let limit = json[JSONKeys.limit.rawValue] as! Int
let size = json[JSONKeys.size.rawValue] as! Int
let linksDict = json[JSONKeys._links.rawValue] as! JSONDictionary
let _links = Links(next: linksDict[JSONKeys.next.rawValue] as? String, prev: linksDict[JSONKeys.prev.rawValue] as? String)
let casesDict = json[JSONKeys.cases.rawValue] as! [JSONDictionary]
var cases = [Case]()
for caseDict in casesDict {
let caseResult = Case.init(json: caseDict)
cases.append(caseResult!)
}

self.init(offset: offset, limit: limit, size: size, cases: cases, _links: _links)
}
}

extension BulkCases: JSONSerializable {

func serialized() -> JSONDictionary {
return [JSONKeys.offset.rawValue: offset,
JSONKeys.limit.rawValue: limit,
JSONKeys.size.rawValue: size,
JSONKeys._links.rawValue: _links,
JSONKeys.cases.rawValue: cases]
}
}
63 changes: 63 additions & 0 deletions Sources/QuizTrain/Models/BulkSections.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// BulkSections.swift
// QuizTrain
//
// Created by Neha Srivastava on 9/26/22.
//

public struct BulkSections {
public let offset: Int
public let limit: Int
public let size: Int
public let sections: [Section]
public let _links: Links
}

// MARK: - JSON Keys
extension BulkSections {

enum JSONKeys: JSONKey {
case offset = "offset"
case limit = "limit"
case size = "size"
case _links = "_links"
case sections = "sections"
case next = "next"
case prev = "prev"
}

}

// MARK: - Serialization
extension BulkSections: JSONDeserializable {

init?(json: JSONDictionary) {

let offset = json[JSONKeys.offset.rawValue] as! Int
let limit = json[JSONKeys.limit.rawValue] as! Int
let size = json[JSONKeys.size.rawValue] as! Int
let linksDict = json[JSONKeys._links.rawValue] as! JSONDictionary
let _links = Links(next: linksDict[JSONKeys.next.rawValue] as? String, prev: linksDict[JSONKeys.prev.rawValue] as? String)
let sectionsDict = json[JSONKeys.sections.rawValue] as! [JSONDictionary]
var sections = [Section]()
for sectionDict in sectionsDict {
if let section = Section.init(json: sectionDict) {
sections.append(section)
}
}

self.init(offset: offset, limit: limit, size: size, sections: sections, _links: _links)
}

}

extension BulkSections: JSONSerializable {

func serialized() -> JSONDictionary {
return [JSONKeys.offset.rawValue: offset,
JSONKeys.limit.rawValue: limit,
JSONKeys.size.rawValue: size,
JSONKeys._links.rawValue: _links,
JSONKeys.sections.rawValue: sections]
}
}
58 changes: 58 additions & 0 deletions Sources/QuizTrain/Models/BulkTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// BulkTests.swift
// QuizTrain
//
// Created by Neha Srivastava on 9/26/22.
//

public struct BulkTests {
public let offset: Int
public let limit: Int
public let size: Int
public let tests: [Test]
public let _links: Links
}

// MARK: - JSON Keys
extension BulkTests {

enum JSONKeys: JSONKey {
case offset = "offset"
case limit = "limit"
case size = "size"
case _links = "_links"
case tests = "tests"
case next = "next"
case prev = "prev"
}

}

// MARK: - Serialization
extension BulkTests: JSONDeserializable {

init?(json: JSONDictionary) {

let offset = json[JSONKeys.offset.rawValue] as! Int
let limit = json[JSONKeys.limit.rawValue] as! Int
let size = json[JSONKeys.size.rawValue] as! Int
let linksDict = json[JSONKeys._links.rawValue] as! JSONDictionary
let _links = Links(next: linksDict[JSONKeys.next.rawValue] as? String, prev: linksDict[JSONKeys.prev.rawValue] as? String)
let testsDict = json[JSONKeys.tests.rawValue] as! [JSONDictionary]
let tests = testsDict.map({ Test.init(json: $0)! })

self.init(offset: offset, limit: limit, size: size, tests: tests, _links: _links)
}

}

extension BulkTests: JSONSerializable {

func serialized() -> JSONDictionary {
return [JSONKeys.offset.rawValue: offset,
JSONKeys.limit.rawValue: limit,
JSONKeys.size.rawValue: size,
JSONKeys._links.rawValue: _links,
JSONKeys.tests.rawValue: tests]
}
}
111 changes: 111 additions & 0 deletions Sources/QuizTrain/Models/QuizTrainData.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
//
// QuizTrainData.swift
// QuizTrain
//
// Created by Neha Srivastava on 9/26/22.
//

/// Data structure that contains the whole data from a QuizTrain project. It can serialize and write/read data from file all at once
public struct QuizTrainData {
public let project: QuizTrain.Project
public let suites: [QuizTrain.Suite]
public let sections: [QuizTrain.Section]
public let cases: [QuizTrain.Case]
public let statuses: [QuizTrain.Status]
public let users: [QuizTrain.User]
public let currentUser: QuizTrain.User

public init(project: QuizTrain.Project, suites: [QuizTrain.Suite], sections: [QuizTrain.Section], cases: [QuizTrain.Case], statuses: [QuizTrain.Status], users: [QuizTrain.User], currentUser: QuizTrain.User) {
self.project = project
self.suites = suites
self.sections = sections
self.cases = cases
self.statuses = statuses
self.users = users
self.currentUser = currentUser
}
}

// MARK: - JSON Keys
extension QuizTrainData {

enum JSONKeys: JSONKey {
case project
case suites
case sections
case cases
case statuses
case users
case currentUser
}

}

// MARK: - Serialization
extension QuizTrainData: JSONDeserializable {

init?(json: JSONDictionary) {

guard let projectJson = json[JSONKeys.project.rawValue] as? JSONDictionary,
let currentUserJson = json[JSONKeys.currentUser.rawValue] as? JSONDictionary,
let usersJson = json[JSONKeys.users.rawValue] as? [JSONDictionary],
let suitesJson = json[JSONKeys.suites.rawValue] as? [JSONDictionary],
let sectionsJson = json[JSONKeys.sections.rawValue] as? [JSONDictionary],
let casesJson = json[JSONKeys.cases.rawValue] as? [JSONDictionary],
let statusesJson = json[JSONKeys.statuses.rawValue] as? [JSONDictionary]
else {
return nil
}

let project = QuizTrain.Project.init(json: projectJson)!
let currentUser = QuizTrain.User.init(json: currentUserJson)!
let users = usersJson.map({ User.init(json: $0)! })
let suites = suitesJson.map({ Suite.init(json: $0)! })
let sections = sectionsJson.map({ Section.init(json: $0)! })
let cases = casesJson.map({ Case.init(json: $0)! })
let statuses = statusesJson.map({ Status.init(json: $0)! })

self.init(project: project, suites: suites, sections: sections, cases: cases, statuses: statuses, users: users, currentUser: currentUser)

}
}

extension QuizTrainData: JSONSerializable {
public func serialized() -> JSONDictionary {
return [JSONKeys.project.rawValue: project.serialized(),
JSONKeys.suites.rawValue: suites.map({ $0.serialized() }),
JSONKeys.sections.rawValue: sections.map({ $0.serialized() }),
JSONKeys.statuses.rawValue: statuses.map({ $0.serialized() }),
JSONKeys.cases.rawValue: cases.map({ $0.serialized() }),
JSONKeys.users.rawValue: users.map({ $0.serialized() }),
JSONKeys.currentUser.rawValue: currentUser.serialized()
]
}
}

extension QuizTrainData {
public static func writeToFile(atPath: URL, quizTrainData: QuizTrainData) {
if !FileManager.default.fileExists(atPath: atPath.path) {
FileManager.default.createFile(atPath: atPath.path, contents: nil)
}
do {
let json = try JSONSerialization.data(withJSONObject: quizTrainData.serialized(), options: .fragmentsAllowed)
try json.write(to: atPath)
} catch {
print("QuizTrain Data Error: Cannot write to file at path: \(atPath)")
return
}
}

public static func readFromFile(atPath: URL) -> QuizTrainData? {
guard let data = try? Data.init(contentsOf: atPath) else {
print("QuizTrain Data Error: Cannot write to file at path: \(atPath)")
return nil
}
guard let json = try? JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) as? JSONDictionary else {
print("QuizTrain Data Error: Cannot convert data to json object")
return nil
}
return QuizTrainData.init(json: json)
}
}
11 changes: 11 additions & 0 deletions Sources/QuizTrain/Models/Types/Links.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// Links.swift
// QuizTrain
//
// Created by Neha Srivastava on 9/26/22.
//

public struct Links {
public let next: String?
public let prev: String?
}
12 changes: 10 additions & 2 deletions Sources/QuizTrain/Network/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -582,8 +582,16 @@ final public class API: NSObject, URLSessionDelegate {
/*
http://docs.gurock.com/testrail-api2/reference-sections#get_sections
*/
@discardableResult public func getSections(projectId: Int, suiteId: Int? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask {
let queryItems = suiteId != nil ? [URLQueryItem(name: "suite_id", value: String(suiteId!))] : nil
@discardableResult public func getSections(projectId: Int, suiteId: Int? = nil, filters: [Filter]? = nil, completionHandler: @escaping (RequestOutcome) -> Void) -> URLSessionDataTask {
var queryItems = [URLQueryItem]()

if let suiteId = suiteId {
queryItems.append(URLQueryItem(name: "suite_id", value: String(suiteId)))
}

if let filters = filters {
_ = filters.compactMap { queryItems.append($0.queryItem) }
}
return get("get_sections/\(projectId)", queryItems: queryItems, completionHandler: completionHandler)
}

Expand Down
Loading

0 comments on commit bb1cd84

Please sign in to comment.