forked from mattpolzin/JSONAPI
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCompoundResource.swift
More file actions
121 lines (109 loc) · 4.41 KB
/
CompoundResource.swift
File metadata and controls
121 lines (109 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//
// CompoundResource.swift
//
//
// Created by Mathew Polzin on 5/25/20.
//
public protocol CompoundResourceProtocol {
associatedtype JSONAPIModel: JSONAPI.ResourceObjectType
associatedtype JSONAPIIncludeType: JSONAPI.Include
var primary: JSONAPIModel { get }
var relatives: [JSONAPIIncludeType] { get }
func filteringRelatives(by filter: (JSONAPIIncludeType) -> Bool) -> Self
}
/// A Resource Object and any relevant related resources. This object
/// is helpful in the context of constructing a Document.
///
/// You can resolve a primary resource and all of the intended includes
/// for that resource and pass them around as a `CompoundResource`
/// prior to constructing a Document.
///
/// Among other things, using this abstraction means you do not need to
/// specialized for a single or batch document at the same time as you are
/// resolving (i.e. materializing or decoding) one or more resources and its
/// relatives.
///
/// - Important: This type is not intended to guarantee
/// that all `relationships` of the primary resource are available
/// in the `relatives` array.
public struct CompoundResource<JSONAPIModel: JSONAPI.ResourceObjectType, JSONAPIIncludeType: JSONAPI.Include>: Equatable, CompoundResourceProtocol {
public let primary: JSONAPIModel
public let relatives: [JSONAPIIncludeType]
public init(primary: JSONAPIModel, relatives: [JSONAPIIncludeType]) {
self.primary = primary
self.relatives = relatives
}
/// Create a new Compound Resource having
/// filtered the relatives by the given closure (which
/// must return `true` for any relative that should
/// remain part of the `CompoundObject`).
///
/// This does not remove relatives from the primary
/// resource's `relationships`, it just filters out
/// which relatives have complete resource objects
/// in the newly created `CompoundResource`.
public func filteringRelatives(by filter: (JSONAPIIncludeType) -> Bool) -> CompoundResource {
return .init(
primary: primary,
relatives: relatives.filter(filter)
)
}
}
extension Sequence where Element: CompoundResourceProtocol {
/// Create new Compound Resources having
/// filtered the relatives by the given closure (which
/// must return `true` for any relative that should
/// remain part of the `CompoundObject`).
///
/// This does not remove relatives from the primary
/// resource's `relationships`, it just filters out
/// which relatives have complete resource objects
/// in the newly created `CompoundResource`.
public func filteringRelatives(by filter: (Element.JSONAPIIncludeType) -> Bool) -> [Element] {
return map { $0.filteringRelatives(by: filter) }
}
}
extension EncodableJSONAPIDocument where PrimaryResourceBody.PrimaryResource: ResourceObjectType {
public typealias CompoundResource = JSONAPI.CompoundResource<PrimaryResourceBody.PrimaryResource, IncludeType>
}
extension SucceedableJSONAPIDocument where PrimaryResourceBody: SingleResourceBodyProtocol, PrimaryResourceBody.PrimaryResource: ResourceObjectType {
public init(
apiDescription: APIDescription,
resource: CompoundResource,
meta: MetaType,
links: LinksType
) {
self.init(
apiDescription: apiDescription,
body: .init(resourceObject: resource.primary),
includes: .init(values: resource.relatives),
meta: meta,
links: links
)
}
}
extension SucceedableJSONAPIDocument where PrimaryResourceBody: ManyResourceBodyProtocol, PrimaryResourceBody.PrimaryResource: ResourceObjectType, IncludeType: Hashable {
public init(
apiDescription: APIDescription,
resources: [CompoundResource],
meta: MetaType,
links: LinksType
) {
var included = Set<Int>()
let includes = resources.reduce(into: [IncludeType]()) { (result, next) in
for include in next.relatives {
if !included.contains(include.hashValue) {
included.insert(include.hashValue)
result.append(include)
}
}
}
self.init(
apiDescription: apiDescription,
body: .init(resourceObjects: resources.map(\.primary)),
includes: .init(values: Array(includes)),
meta: meta,
links: links
)
}
}