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

chore: kickoff release #3778

Merged
merged 1 commit into from
Jul 16, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ import Foundation
/// directly by host applications. The behavior of this may change without warning.
public enum ModelAssociation {
case hasMany(associatedFieldName: String?, associatedFieldNames: [String] = [])
case hasOne(associatedFieldName: String?, targetNames: [String])
case hasOne(associatedFieldName: String?, associatedFieldNames: [String] = [], targetNames: [String])
case belongsTo(associatedFieldName: String?, targetNames: [String])

public static let belongsTo: ModelAssociation = .belongsTo(associatedFieldName: nil, targetNames: [])
Expand All @@ -108,14 +108,20 @@ public enum ModelAssociation {
)
}

@available(*, deprecated, message: "Use hasOne(associatedWith:targetNames:)")
@available(*, deprecated, message: "Use hasOne(associatedWith:associatedFields:targetNames:)")
public static func hasOne(associatedWith: CodingKey?, targetName: String? = nil) -> ModelAssociation {
let targetNames = targetName.map { [$0] } ?? []
return .hasOne(associatedWith: associatedWith, targetNames: targetNames)
}

public static func hasOne(associatedWith: CodingKey?, targetNames: [String] = []) -> ModelAssociation {
return .hasOne(associatedFieldName: associatedWith?.stringValue, targetNames: targetNames)
public static func hasOne(
associatedWith: CodingKey? = nil,
associatedFields: [CodingKey] = [],
targetNames: [String] = []) -> ModelAssociation {
return .hasOne(
associatedFieldName: associatedWith?.stringValue,
associatedFieldNames: associatedFields.map { $0.stringValue },
targetNames: targetNames)
}

@available(*, deprecated, message: "Use belongsTo(associatedWith:targetNames:)")
Expand Down Expand Up @@ -254,7 +260,7 @@ extension ModelField {
let associatedModel = requiredAssociatedModelName
switch association {
case .belongsTo(let associatedKey, _),
.hasOne(let associatedKey, _),
.hasOne(let associatedKey, _, _),
.hasMany(let associatedKey, _):
// TODO handle modelName casing (convert to camelCase)
let key = associatedKey ?? associatedModel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,21 @@ public enum ModelFieldDefinition {
association: .hasOne(associatedWith: associatedKey, targetNames: targetNames))
}

public static func hasOne(_ key: CodingKey,
is nullability: ModelFieldNullability = .required,
isReadOnly: Bool = false,
ofType type: Model.Type,
associatedFields associatedKeys: [CodingKey],
targetNames: [String] = []) -> ModelFieldDefinition {
return .field(key,
is: nullability,
isReadOnly: isReadOnly,
ofType: .model(type: type),
association: .hasOne(associatedWith: associatedKeys.first,
associatedFields: associatedKeys,
targetNames: targetNames))
}

public static func belongsTo(_ key: CodingKey,
is nullability: ModelFieldNullability = .required,
isReadOnly: Bool = false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ public class AppSyncListProvider<Element: Model>: ModelListProvider {
}
let defaultFieldName = modelSchema.name.camelCased() + field.pascalCased() + "Id"
switch modelField.association {
case .belongsTo(_, let targetNames), .hasOne(_, let targetNames):
case .belongsTo(_, let targetNames), .hasOne(_, _, let targetNames):
guard !targetNames.isEmpty else {
return [defaultFieldName]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ extension Customer4 {
model.fields(
.field(customer4.id, is: .required, ofType: .string),
.field(customer4.name, is: .optional, ofType: .string),
.hasOne(customer4.activeCart, is: .optional, ofType: Cart4.self, associatedWith: Cart4.keys.customer),
.hasOne(customer4.activeCart, is: .optional, ofType: Cart4.self, associatedFields: [Cart4.keys.customer]),
.field(customer4.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime),
.field(customer4.updatedAt, is: .optional, isReadOnly: true, ofType: .dateTime)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ extension Model {
let defaultFieldName = modelName.camelCased() + modelField.name.pascalCased() + "Id"
if case let .belongsTo(_, targetNames) = modelField.association, !targetNames.isEmpty {
return targetNames
} else if case let .hasOne(_, targetNames) = modelField.association,
} else if case let .hasOne(_, _, targetNames) = modelField.association,
!targetNames.isEmpty {
return targetNames
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ extension QueryPredicateOperation: GraphQLFilterConvertible {
}
let targetName = targetNames.first ?? defaultFieldName
return targetName
case .hasOne(_, let targetNames):
case .hasOne(_, _, let targetNames):
guard targetNames.count == 1 else {
preconditionFailure("QueryPredicate not supported on associated field with composite key: \(field)")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ extension ModelField: SQLColumn {
var sqlName: String {
if case let .belongsTo(_, targetNames) = association {
return foreignKeySqlName(withAssociationTargets: targetNames)
} else if case let .hasOne(_, targetNames) = association {
} else if case let .hasOne(_, _, targetNames) = association {
return foreignKeySqlName(withAssociationTargets: targetNames)
}
return name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ extension Statement: StatementModelConvertible {

private func getTargetNames(field: ModelField) -> [String] {
switch field.association {
case let .some(.hasOne(_, targetNames)):
case let .some(.hasOne(_, _, targetNames)):
return targetNames
case let .some(.belongsTo(_, targetNames)):
return targetNames
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,37 @@ class ModelFieldAssociationTests: XCTestCase {

func testHasOneWithCodingKeys() {
let hasOne = ModelAssociation.hasOne(associatedWith: Comment.keys.post, targetNames: [])
guard case .hasOne(let fieldName, let target) = hasOne else {
guard case .hasOne(let fieldName, let fieldNames, let target) = hasOne else {
XCTFail("Should create hasOne association")
return
}
XCTAssertEqual(fieldName, Comment.keys.post.stringValue)
XCTAssertEqual(fieldNames, [])
XCTAssertEqual(target, [])
}

func testHasOneWithCodingKeysWithTargetName() {
let hasOne = ModelAssociation.hasOne(associatedWith: Comment.keys.post, targetNames: ["postID"])
guard case .hasOne(let fieldName, let target) = hasOne else {
guard case .hasOne(let fieldName, let fieldNames, let target) = hasOne else {
XCTFail("Should create hasOne association")
return
}
XCTAssertEqual(fieldName, Comment.keys.post.stringValue)
XCTAssertEqual(fieldNames, [])
XCTAssertEqual(target, ["postID"])
}

func testHasOneWithCodingKeysWithAssociatedFields() {
let hasOne = ModelAssociation.hasOne(associatedFields: [Comment.keys.post])
guard case .hasOne(let fieldName, let fieldNames, let target) = hasOne else {
XCTFail("Should create hasOne association")
return
}
XCTAssertEqual(fieldName, nil)
XCTAssertEqual(fieldNames, ["post"])
XCTAssertEqual(target, [])
}

func testBelongsToWithTargetName() {
let belongsTo = ModelAssociation.belongsTo(targetName: "postID")
guard case .belongsTo(let fieldName, let target) = belongsTo else {
Expand Down
Loading