-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
8 changed files
with
592 additions
and
145 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Petr Pavlik on 11.03.2024. | ||
// | ||
|
||
import Fluent | ||
|
||
struct CreateOrganizationInvite: AsyncMigration { | ||
func prepare(on database: Database) async throws { | ||
|
||
try await database.schema(OrganizationInvite.schema) | ||
.id() | ||
.field(.email, .string, .required) | ||
.field(.role, .string, .required) | ||
.field(.createdAt, .datetime) | ||
.field(.updatedAt, .datetime) | ||
.field(.organizationId, .uuid, .references(Organization.schema, "id", onDelete: .cascade)) | ||
.unique(on: .email) | ||
.create() | ||
} | ||
|
||
func revert(on database: Database) async throws { | ||
try await database.schema(OrganizationInvite.schema).delete() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Petr Pavlik on 11.03.2024. | ||
// | ||
|
||
import Fluent | ||
import Vapor | ||
|
||
final class OrganizationInvite: Model, Content { | ||
static let schema = "organization_invites" | ||
|
||
@ID(key: .id) | ||
var id: UUID? | ||
|
||
@Field(key: .email) | ||
var email: String | ||
|
||
@Field(key: .role) | ||
var role: ProfileOrganizationRole.Role | ||
|
||
@Timestamp(key: .createdAt, on: .create) | ||
var createdAt: Date? | ||
|
||
@Timestamp(key: .updatedAt, on: .update) | ||
var updatedAt: Date? | ||
|
||
@Parent(key: .organizationId) | ||
var organization: Organization | ||
|
||
init() { } | ||
|
||
init(id: UUID? = nil, email: String, role: ProfileOrganizationRole.Role, organization: Organization) throws { | ||
self.id = id | ||
self.email = email | ||
self.role = role | ||
self.$organization.id = try organization.requireID() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// | ||
// File.swift | ||
// | ||
// | ||
// Created by Petr Pavlik on 11.03.2024. | ||
// | ||
|
||
import Foundation | ||
|
||
extension SkippableEncoding : Sendable where Wrapped : Sendable {} | ||
extension SkippableEncoding : Hashable where Wrapped : Hashable {} | ||
extension SkippableEncoding : Equatable where Wrapped : Equatable {} | ||
|
||
@propertyWrapper | ||
public enum SkippableEncoding<Wrapped : Codable> : Codable { | ||
|
||
case skipped | ||
case encoded(Wrapped?) | ||
|
||
public init() { | ||
self = .skipped | ||
} | ||
|
||
public var wrappedValue: Wrapped? { | ||
get { | ||
switch self { | ||
case .skipped: return nil | ||
case .encoded(let v): return v | ||
} | ||
} | ||
set { | ||
self = .encoded(newValue) | ||
} | ||
} | ||
|
||
public var projectedValue: Self { | ||
get {self} | ||
set {self = newValue} | ||
} | ||
|
||
/** Returns `.none` if the value is skipped, `.some(wrappedValue)` if it is not. */ | ||
public var value: Wrapped?? { | ||
switch self { | ||
case .skipped: return nil | ||
case .encoded(let v): return .some(v) | ||
} | ||
} | ||
|
||
public init(from decoder: Decoder) throws { | ||
let container = try decoder.singleValueContainer() | ||
self = try .encoded(container.decode(Wrapped?.self)) | ||
} | ||
|
||
public func encode(to encoder: Encoder) throws { | ||
/* The encoding is taken care of in KeyedEncodingContainer. */ | ||
assertionFailure() | ||
|
||
switch self { | ||
case .skipped: | ||
(/*nop*/) | ||
|
||
case .encoded(let v): | ||
var container = encoder.singleValueContainer() | ||
try container.encode(v) | ||
} | ||
} | ||
|
||
} | ||
|
||
extension KeyedEncodingContainer { | ||
|
||
public mutating func encode<Wrapped>(_ value: SkippableEncoding<Wrapped>, forKey key: KeyedEncodingContainer<K>.Key) throws { | ||
switch value { | ||
case .skipped: (/*nop*/) | ||
case .encoded(let v): try encode(v, forKey: key) | ||
} | ||
} | ||
|
||
} | ||
|
||
extension UnkeyedEncodingContainer { | ||
|
||
mutating func encode<Wrapped>(_ value: SkippableEncoding<Wrapped>) throws { | ||
switch value { | ||
case .skipped: (/*nop*/) | ||
case .encoded(let v): try encode(v) | ||
} | ||
} | ||
|
||
} | ||
|
||
extension SingleValueEncodingContainer { | ||
|
||
mutating func encode<Wrapped>(_ value: SkippableEncoding<Wrapped>) throws { | ||
switch value { | ||
case .skipped: (/*nop*/) | ||
case .encoded(let v): try encode(v) | ||
} | ||
} | ||
|
||
} | ||
|
||
extension KeyedDecodingContainer { | ||
|
||
public func decode<Wrapped>(_ type: SkippableEncoding<Wrapped>.Type, forKey key: Key) throws -> SkippableEncoding<Wrapped> { | ||
/* So IMHO: | ||
* if let decoded = try decodeIfPresent(SkippableEncoding<Wrapped>?.self, forKey: key) { | ||
* return decoded ?? SkippableEncoding.encoded(nil) | ||
* } | ||
* should definitely work, but it does not (when the key is present but the value nil, we do not get in the if. | ||
* So instead we try and decode nil directly. | ||
* If that fails (missing key), we fallback to decoding the SkippableEncoding directly if the key is present. */ | ||
if (try? decodeNil(forKey: key)) == true { | ||
return SkippableEncoding.encoded(nil) | ||
} | ||
return try decodeIfPresent(SkippableEncoding<Wrapped>.self, forKey: key) ?? SkippableEncoding() | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.