Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relese/v2.2.0 #30

Merged
merged 13 commits into from
Nov 15, 2019
Prev Previous commit
Next Next commit
Meta in encoding solving issue #27
  • Loading branch information
Truba committed Nov 14, 2019
commit 6725368fce09b6d1fb7577bff92aad1d47a9e492
13 changes: 9 additions & 4 deletions Japx/Classes/Codable/JapxCodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,24 @@ public final class JapxEncoder {
// Underlying JSONEncoder, can be used to add date formats, ...
public let jsonEncoder: JSONEncoder

/// Options specifying how `Japx.Encoder` should encode JSON into JSON:API.
public let options: JapxEncodingOptions

/// Initializes `self` with underlying `JSONEncoder` instance
public init(jsonEncoder: JSONEncoder = JSONEncoder()) {
public init(jsonEncoder: JSONEncoder = JSONEncoder(), options: JapxEncodingOptions = .default) {
self.jsonEncoder = jsonEncoder
self.options = options
}

/// Encodes the given top-level value and returns its JSON:API representation.
///
/// - parameter value: The value to encode.
/// - parameter additionalParams: Additional [String: Any] to add with `data` to JSON:API object.
/// - returns: A new `[String: Any]` value containing the encoded JSON:API data.
/// - throws: An error if any value throws an error during encoding.
public func encode<T>(_ value: T) throws -> Parameters where T : Encodable {
public func encode<T>(_ value: T, additionalParams: Parameters? = nil) throws -> Parameters where T : Encodable {
let data = try jsonEncoder.encode(value)
return try Japx.Encoder.encode(data: data)
return try Japx.Encoder.encode(data: data, additionalParams: additionalParams, options: options)
}
}

Expand All @@ -51,7 +56,7 @@ public final class JapxDecoder {
/// Options specifying how `Japx.Decoder` should decode JSON:API into JSON.
public let options: JapxDecodingOptions

/// Initializes `self` with underlying `JSONDecoder` instance
/// Initializes `self` with underlying `JSONDecoder` instance and `JapxDecodingOptions`
public init(jsonDecoder: JSONDecoder = JSONDecoder(), options: JapxDecodingOptions = .default) {
self.jsonDecoder = jsonDecoder
self.options = options
Expand Down
77 changes: 66 additions & 11 deletions Japx/Classes/Core/Japx.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public struct JapxDecodingOptions {

/// Creates an instance with the specified properties.
///
/// - parameter parseNotIncludedRelationships: Read more [here]MissingRelationshipObjects-JsonApi
/// - parameter parseNotIncludedRelationships: Read more [here](parseNotIncludedRelationships)
///
/// - returns: The new `JapxDecodingOptions` instance.
public init(parseNotIncludedRelationships: Bool = false) {
Expand All @@ -55,6 +55,35 @@ public extension JapxDecodingOptions {
static var `default`: JapxDecodingOptions { .init() }
}

/// `JapxEncodingOptions` is a set of options affecting the encoding of JSON into JSON:API you requested from `Japx.Encoder`.
public struct JapxEncodingOptions {
Truba marked this conversation as resolved.
Show resolved Hide resolved

/// Common namespace inclued all attribute names, relationship names, `type` and `id`.
Truba marked this conversation as resolved.
Show resolved Hide resolved
/// If enabled it will include keyword `meta` into common namepace, expecit the not to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what did you say here after into common namepace, (typo namepace -> namespace), so please reformulate. I'm not sure if you are trying to say that client should not expect to have meta as an attribute or relationship name or something else

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated, please check if it look ok now?

/// have `mata` as an attribute or renationship name.
/// It will then encode meta on the same level as `attributes` and `relationships`
///
/// Defaults to false.
///
/// - Tag: includeMetaToCommonNamespce
public var includeMetaToCommonNamespce: Bool = false

/// Creates an instance with the specified properties.
///
/// - parameter includeMetaToCommonNamespce: Read more [here](includeMetaToCommonNamespce)
///
/// - returns: The new `JapxDecodingOptions` instance.
public init(includeMetaToCommonNamespce: Bool = false) {
self.includeMetaToCommonNamespce = includeMetaToCommonNamespce
}
}

public extension JapxEncodingOptions {

/// Default JSON to JSON:API decoding options for `Japx.Encoder`
static var `default`: JapxEncodingOptions { .init() }
}

private struct Consts {

struct APIKeys {
Expand All @@ -64,6 +93,7 @@ private struct Consts {
static let included = "included"
static let relationships = "relationships"
static let attributes = "attributes"
static let meta = "meta"
}

struct General {
Expand Down Expand Up @@ -178,15 +208,16 @@ public extension Japx.Encoder {
///
/// - parameter data: JSON object as Data.
/// - parameter additionalParams: Additional [String: Any] to add with `data` to JSON:API object.
/// - parameter options: Options specifying how `Japx.Encoder` should encode JSON into JSON:API.
///
/// - returns: JSON:API object.
static func encode(data: Data, additionalParams: Parameters? = nil) throws -> Parameters {
static func encode(data: Data, additionalParams: Parameters? = nil, options: JapxEncodingOptions = .default) throws -> Parameters {
let json = try JSONSerialization.jsonObject(with: data)
if let jsonObject = json as? Parameters {
return try encode(json: jsonObject, additionalParams: additionalParams)
return try encode(json: jsonObject, additionalParams: additionalParams, options: options)
}
if let jsonArray = json as? [Parameters] {
return try encode(json: jsonArray, additionalParams: additionalParams)
return try encode(json: jsonArray, additionalParams: additionalParams, options: options)
}
throw JapxError.unableToConvertDataToJson(data: json)
}
Expand All @@ -195,23 +226,25 @@ public extension Japx.Encoder {
///
/// - parameter json: JSON object.
/// - parameter additionalParams: Additional [String: Any] to add with `data` to JSON:API object.
/// - parameter options: Options specifying how `Japx.Encoder` should encode JSON into JSON:API.
///
/// - returns: JSON:API object.
static func encode(json: Parameters, additionalParams: Parameters? = nil) throws -> Parameters {
static func encode(json: Parameters, additionalParams: Parameters? = nil, options: JapxEncodingOptions = .default) throws -> Parameters {
var params = additionalParams ?? [:]
params[Consts.APIKeys.data] = try encodeAttributesAndRelationships(on: json)
params[Consts.APIKeys.data] = try encodeAttributesAndRelationships(on: json, options: options)
return params
}

/// Converts simple flat JSON object to JSON:API object.
///
/// - parameter json: JSON objects represented as Array.
/// - parameter additionalParams: Additional [String: Any] to add with `data` to JSON:API object.
/// - parameter options: Options specifying how `Japx.Encoder` should encode JSON into JSON:API.
///
/// - returns: JSON:API object.
static func encode(json: [Parameters], additionalParams: Parameters? = nil) throws -> Parameters {
static func encode(json: [Parameters], additionalParams: Parameters? = nil, options: JapxEncodingOptions = .default) throws -> Parameters {
var params = additionalParams ?? [:]
params[Consts.APIKeys.data] = try json.compactMap { try encodeAttributesAndRelationships(on: $0) as AnyObject }
params[Consts.APIKeys.data] = try json.compactMap { try encodeAttributesAndRelationships(on: $0, options: options) as AnyObject }
return params
}
}
Expand Down Expand Up @@ -434,13 +467,22 @@ private extension Japx.Decoder {

private extension Japx.Encoder {

static func encodeAttributesAndRelationships(on jsonObject: Parameters) throws -> Parameters {
static func encodeAttributesAndRelationships(on jsonObject: Parameters, options: JapxEncodingOptions) throws -> Parameters {
var object = jsonObject
var attributes = Parameters()
var relationships = Parameters()
let objectKeys = object.keys

let relationshipExtractor = extractRelationshipData(
includeMetaToCommonNamespce: options.includeMetaToCommonNamespce
)

for key in objectKeys where key != Consts.APIKeys.type && key != Consts.APIKeys.id {

if options.includeMetaToCommonNamespce && key == Consts.APIKeys.meta {
continue
}

if let array = object.asArray(from: key) {
let isArrayOfRelationships = array.first?.containsTypeAndId() ?? false
if !isArrayOfRelationships {
Expand All @@ -449,7 +491,7 @@ private extension Japx.Encoder {
object.removeValue(forKey: key)
continue
}
let dataArray = try array.map { try $0.asDataWithTypeAndId() }
let dataArray = try array.map(relationshipExtractor)
// Handle relationships array
relationships[key] = [Consts.APIKeys.data: dataArray]
object.removeValue(forKey: key)
Expand All @@ -462,7 +504,7 @@ private extension Japx.Encoder {
object.removeValue(forKey: key)
continue
}
let dataObj = try obj.asDataWithTypeAndId()
let dataObj = try relationshipExtractor(obj)
// Handle relationship object
relationships[key] = [Consts.APIKeys.data: dataObj]
object.removeValue(forKey: key)
Expand All @@ -475,6 +517,19 @@ private extension Japx.Encoder {
object[Consts.APIKeys.relationships] = relationships
return object
}

static func extractRelationshipData(includeMetaToCommonNamespce: Bool) -> (Parameters) throws -> (Any) {
if !includeMetaToCommonNamespce {
return { try $0.asDataWithTypeAndId() }
}
return { object in
var params = try object.asDataWithTypeAndId()
if let meta = object[Consts.APIKeys.meta] {
params[Consts.APIKeys.meta] = meta
}
return params
}
}
}

// MARK: - General helper extensions -
Expand Down