diff --git a/Package.swift b/Package.swift index 4630f747..38058ec4 100644 --- a/Package.swift +++ b/Package.swift @@ -22,7 +22,7 @@ let package = Package( .package(url: "https://github.com/vapor/service.git", from: "1.0.0"), // *️⃣ Build SQL queries in Swift. Extensible, protocol-based design that supports DQL, DML, and DDL. - .package(url: "https://github.com/vapor/sql.git", from: "2.0.0-beta"), + .package(url: "https://github.com/vapor/sql.git", from: "2.0.0"), ], targets: [ .target(name: "Fluent", dependencies: ["Async", "Console", "Command", "Core", "DatabaseKit", "Logging", "Service"]), diff --git a/Sources/Fluent/Cache/KeyedCacheSupporting+Fluent.swift b/Sources/Fluent/Cache/KeyedCacheSupporting+Fluent.swift index f5c16f1a..794abb4c 100644 --- a/Sources/Fluent/Cache/KeyedCacheSupporting+Fluent.swift +++ b/Sources/Fluent/Cache/KeyedCacheSupporting+Fluent.swift @@ -17,10 +17,17 @@ extension KeyedCacheSupporting where Self: QuerySupporting { public static func keyedCacheSet(_ key: String, to encodable: E, on conn: Self.Connection) throws -> Future where E: Encodable { - let data = try JSONEncoder().encode(Encode(data: encodable)) - return CacheEntry(key: key, data: data) - .create(on: conn) - .transform(to: ()) + return CacheEntry.find(key, on: conn).flatMap { existing -> Future> in + let data = try JSONEncoder().encode(Encode(data: encodable)) + if let existing = existing { + existing.data = data + return existing.update(on: conn) + } else { + // create new entry + return CacheEntry(key: key, data: data) + .create(on: conn) + } + }.transform(to: ()) } /// See `KeyedCacheSupporting`. diff --git a/Sources/Fluent/Model/Pivot.swift b/Sources/Fluent/Model/Pivot.swift index 21f7715a..d650f2e1 100644 --- a/Sources/Fluent/Model/Pivot.swift +++ b/Sources/Fluent/Model/Pivot.swift @@ -66,5 +66,6 @@ extension Pivot { /// A pivot that can be initialized from just the left and right models. This allows /// Fluent to automatically create pivots for extended functionality. ex: attaching. public protocol ModifiablePivot: Pivot { + /// Creates an instance of `Self` using the left and right related models. init(_ left: Left, _ right: Right) throws } diff --git a/Sources/Fluent/Query/QuerySupporting.swift b/Sources/Fluent/Query/QuerySupporting.swift index 2160cbf8..5c15b619 100644 --- a/Sources/Fluent/Query/QuerySupporting.swift +++ b/Sources/Fluent/Query/QuerySupporting.swift @@ -230,6 +230,7 @@ public protocol QuerySupporting: Database { /// || static var queryFilterRelationOr: QueryFilterRelation { get } + /// Applies a new default filter relation to the query. static func queryDefaultFilterRelation(_ relation: QueryFilterRelation, on: inout Query) /// Creates an instance of `QueryFilter` from a relation and an array of other filters. diff --git a/Sources/Fluent/Schema/DatabasesConfig+References.swift b/Sources/Fluent/Schema/DatabasesConfig+References.swift index e26be9a3..288894ff 100644 --- a/Sources/Fluent/Schema/DatabasesConfig+References.swift +++ b/Sources/Fluent/Schema/DatabasesConfig+References.swift @@ -1,8 +1,10 @@ extension DatabasesConfig { + /// Disables references on the specified database. public mutating func enableReferences(on db: DatabaseIdentifier) where D: SchemaSupporting { appendConfigurationHandler(on: db) { D.enableReferences(on: $0) } } + /// Enables references on the specified database. public mutating func disableReferences(on db: DatabaseIdentifier) where D: SchemaSupporting { appendConfigurationHandler(on: db) { D.disableReferences(on: $0) } } diff --git a/Sources/Fluent/Schema/SchemaBuilder.swift b/Sources/Fluent/Schema/SchemaBuilder.swift index 0aca8cd3..01c51793 100644 --- a/Sources/Fluent/Schema/SchemaBuilder.swift +++ b/Sources/Fluent/Schema/SchemaBuilder.swift @@ -44,17 +44,26 @@ extension SchemaBuilder { } + /// Adds a field with specified type. + /// + /// builder.field(for: \.name, type: ...) + /// + /// - parameters: + /// - key: `KeyPath` to the field. + /// - type: Data type to use for this field. public func field(for key: KeyPath, type: Model.Database.SchemaFieldType) { let field = Model.Database.schemaField(Model.Database.queryField(.keyPath(key)), type) self.field(field) } + /// Adds a custom field. public func field(_ field: Model.Database.SchemaField) { Model.Database.schemaFieldCreate(field, to: &schema) } // MARK: Constraint + /// Adds a custom constraint. public func constraint(_ constraint: Model.Database.SchemaConstraint) { Model.Database.schemaConstraintCreate(constraint, to: &schema) } diff --git a/Sources/Fluent/Schema/SchemaSupporting.swift b/Sources/Fluent/Schema/SchemaSupporting.swift index 4ebf5b19..c256085d 100644 --- a/Sources/Fluent/Schema/SchemaSupporting.swift +++ b/Sources/Fluent/Schema/SchemaSupporting.swift @@ -1,39 +1,57 @@ /// SQL database. public protocol SchemaSupporting: QuerySupporting { + /// Associated schema type. associatedtype Schema + /// Associated schema action type. associatedtype SchemaAction + /// Associated schema field type. associatedtype SchemaField + /// Associated schema field data type. associatedtype SchemaFieldType + /// Associated schema constraint type. associatedtype SchemaConstraint + /// Associated reference action type. associatedtype SchemaReferenceAction + /// Create schema action. static var schemaActionCreate: SchemaAction { get } + /// Update schema action. static var schemaActionUpdate: SchemaAction { get } + /// Delete schema action. static var schemaActionDelete: SchemaAction { get } + /// Creates a schema. static func schemaCreate(_ action: SchemaAction, _ entity: String) -> Schema + /// Creates a schema field. static func schemaField(for type: Any.Type, isIdentifier: Bool, _ field: QueryField) -> SchemaField + /// Creates a schema field. static func schemaField(_ field: QueryField, _ type: SchemaFieldType) -> SchemaField + /// Creates a field on the schema. static func schemaFieldCreate(_ field: SchemaField, to query: inout Schema) + /// Deletes a field on the schema. static func schemaFieldDelete(_ field: QueryField, to query: inout Schema) + /// Creates a reference constraint. static func schemaReference(from: QueryField, to: QueryField, onUpdate: SchemaReferenceAction?, onDelete: SchemaReferenceAction?) -> SchemaConstraint + /// Creates a unique constraint. static func schemaUnique(on: [QueryField]) -> SchemaConstraint + /// Creates a constraint on the schema. static func schemaConstraintCreate(_ constraint: SchemaConstraint, to query: inout Schema) + /// Deletes a constraint on the schema. static func schemaConstraintDelete(_ constraint: SchemaConstraint, to query: inout Schema) /// Executes the supplied schema on the database connection. diff --git a/Sources/Fluent/Schema/SchemaUpdater.swift b/Sources/Fluent/Schema/SchemaUpdater.swift index d6878dd9..2fa7b9c1 100644 --- a/Sources/Fluent/Schema/SchemaUpdater.swift +++ b/Sources/Fluent/Schema/SchemaUpdater.swift @@ -20,6 +20,7 @@ public final class SchemaUpdater: SchemaBuilder where Model: Fluent.Model // MARK: Constraint + /// Deletes a custom constraint. public func deleteConstraint(_ constraint: Model.Database.SchemaConstraint) { Model.Database.schemaConstraintDelete(constraint, to: &schema) } diff --git a/Sources/Fluent/Utilities/Deprecated.swift b/Sources/Fluent/Utilities/Deprecated.swift index 6c8e28f8..e290b83e 100644 --- a/Sources/Fluent/Utilities/Deprecated.swift +++ b/Sources/Fluent/Utilities/Deprecated.swift @@ -1,135 +1 @@ -/// - warning: Deprecated. -@available(*, deprecated, renamed: "CacheEntry") -public typealias FluentCacheEntry = CacheEntry - -extension Model { - /// - warning: Deprecated. - @available(*, deprecated, renamed: "TimestampKey") - public typealias CreatedAtKey = TimestampKey - - /// - warning: Deprecated. - @available(*, deprecated, renamed: "TimestampKey") - public typealias UpdatedAtKey = TimestampKey - - /// - warning: Deprecated. - @available(*, deprecated, renamed: "TimestampKey") - public typealias DeletedAtKey = TimestampKey - - /// - warning: Deprecated. - @available(*, deprecated, message: "This method is redundant and will be removed. Use static method on Model instead: User.query(on:)") - public func query(on conn: DatabaseConnectable) -> QueryBuilder { - return Self.query(on: conn) - } -} - -extension TransactionSupporting { - /// - warning: Deprecated. - @available(*, deprecated, message: "Use the new `DatabaseConnectable.transaction(...)` method instead.") - public static func transaction(_ transaction: @escaping (Connection) throws -> Future, on conn: Connection) -> Future { - return transactionExecute(transaction, on: conn) - } -} - -extension QueryBuilder { - /// - warning: Deprecated. - @available(*, deprecated, message: "Use Model.query(on:withSoftDeleted:)") - public func withSoftDeleted() -> QueryBuilder { - fatalError("Use Model.query(on:withSoftDeleted:)") - } -} - -extension Model { - /// - warning: Deprecated. - @available(*, deprecated, renamed: "delete(force:on:)") - public func forceDelete(on conn: Database.Connection) -> Future { - return delete(force: true, on: conn) - } -} - -/// - warning: Deprecated. -@available(*, deprecated, message: "Model now supports timestamps via an _optional_ static key.") -public protocol Timestampable { } - -/// - warning: Deprecated. -@available(*, deprecated, message: "Model now supports soft-deletion via an _optional_ static key.") -public protocol SoftDeletable { } - -extension DatabaseConnectable { - /// - warning: Deprecated. - @available(*, deprecated, message: "This method is redundant and will be removed. Use static method on Model instead: User.query(on:)") - public func query(_ model: Model.Type) -> QueryBuilder where Model: Fluent.Model { - return Model.query(on: self) - } -} - - -extension SchemaBuilder { - /// - warning: Deprecated. - @available(*, deprecated, renamed: "reference(from:to:onUpdate:onDelete:)") - public func addReference(from base: KeyPath, to referenced: KeyPath, actions: Model.Database.SchemaReferenceAction? = nil) - where Other: Fluent.Model - { - assert(actions == nil, "use reference(from:to:onUpdate:onDelete:)") - reference(from: base, to: referenced) - } - - /// - warning: Deprecated. - @available(*, deprecated, renamed: "reference(from:to:onUpdate:onDelete:)") - public func addReference(from base: KeyPath, to referenced: KeyPath, actions: Model.Database.SchemaReferenceAction? = nil) - where Other: Fluent.Model - { - assert(actions == nil, "use reference(from:to:onUpdate:onDelete:)") - reference(from: base, to: referenced) - } - - /// - warning: Deprecated. - @available(*, deprecated, renamed: "reference(from:to:onUpdate:onDelete:)") - public func addReference(from base: KeyPath, to referenced: KeyPath, actions: Model.Database.SchemaReferenceAction? = nil) - where Other: Fluent.Model - { - assert(actions == nil, "use reference(from:to:onUpdate:onDelete:)") - reference(from: base, to: referenced) - } - - /// - warning: Deprecated. - @available(*, deprecated, renamed: "reference(for:type:)") - public func field(type: Model.Database.SchemaFieldType, for key: KeyPath) { - field(for: key, type: type) - } -} - - -extension SchemaBuilder { - /// - warning: Deprecated. - @available(*, deprecated, renamed: "unique(on:)") - public func addIndex(to: KeyPath, isUnique: Bool = false) { - return unique(on: to) - } - /// - warning: Deprecated. - @available(*, deprecated, renamed: "unique(on:_:)") - public func addIndex(to: KeyPath, _ and: KeyPath, isUnique: Bool = false) { - return unique(on: to, and) - } -} - -extension SchemaUpdater { - /// - warning: Deprecated. - @available(*, deprecated, renamed: "deleteField(for:)") - public func removeField(for field: KeyPath) { - self.deleteField(for: field) - } - - /// - warning: Deprecated. - @available(*, deprecated, renamed: "deleteField(_:)") - public func removeField(_ column: Model.Database.QueryField) { - self.deleteField(column) - } -} - -extension DatabasesConfig { - /// - warning: Deprecated. - @available(*, deprecated, renamed: "enableReferences(on:)") - public mutating func enableReferebces(on db: DatabaseIdentifier) where D: SchemaSupporting { - return enableReferences(on: db) - } -} +/// Nothing here... yet diff --git a/Sources/FluentSQL/QueryBuilder+GroupBy.swift b/Sources/FluentSQL/QueryBuilder+GroupBy.swift index 4f461f1b..c3c462f8 100644 --- a/Sources/FluentSQL/QueryBuilder+GroupBy.swift +++ b/Sources/FluentSQL/QueryBuilder+GroupBy.swift @@ -1,4 +1,8 @@ extension QueryBuilder where Database.Query: FluentSQLQuery, Result: SQLTable { + /// Adds a SQL group by to the query. + /// + /// groupBy(\.name) + /// public func groupBy(_ field: KeyPath) -> Self { query.groupBy.append(.groupBy(.column(.keyPath(field)))) return self diff --git a/Sources/FluentSQL/SchemaBuilder+Field.swift b/Sources/FluentSQL/SchemaBuilder+Field.swift index bfaf9b1b..dbef8695 100644 --- a/Sources/FluentSQL/SchemaBuilder+Field.swift +++ b/Sources/FluentSQL/SchemaBuilder+Field.swift @@ -2,6 +2,14 @@ extension SchemaBuilder where Model.Database.Schema: FluentSQLSchema, Model.Database.SchemaField == Model.Database.Schema.ColumnDefinition { + /// Adds a field with specified type and constraints. + /// + /// builder.field(for: \.name, type: ..., ...) + /// + /// - parameters: + /// - key: `KeyPath` to the field. + /// - type: Data type to use for this field. + /// - constraints: Constraints to apply to this field. public func field( for key: KeyPath, type: Model.Database.Schema.ColumnDefinition.DataType, @@ -11,6 +19,14 @@ extension SchemaBuilder where self.field(.columnDefinition(.column(nil, .identifier(property.path[0])), type, [.notNull] + constraints)) } + /// Adds a field with specified type and constraints. + /// + /// builder.field(for: \.name, type: ..., ...) + /// + /// - parameters: + /// - key: `KeyPath` to the field. + /// - type: Data type to use for this field. + /// - constraints: Constraints to apply to this field. public func field( for key: KeyPath, type: Model.Database.Schema.ColumnDefinition.DataType,