Skip to content
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
4 changes: 4 additions & 0 deletions .env.testing
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
PUSH_SECURITY_CODE=1234
S3_BUCKET_NAME=swiftleeds-speakers
CHECKIN_SECRET=1234
ENABLE_AASA=true
8 changes: 8 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,16 @@ concurrency:
cancel-in-progress: true

jobs:
lint:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: SwiftFormat
run: swiftformat --lint . --reporter github-actions-log

build:
if: github.repository_owner == 'SwiftLeeds'
needs: lint
runs-on: ubuntu-latest
container: swift:6.0-jammy

Expand Down
56 changes: 56 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Test Application

on:
pull_request:
push:
branches: [main]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
test:
if: github.repository_owner == 'SwiftLeeds'
runs-on: ubuntu-latest
container: swift:6.0-jammy

steps:
- name: Checkout Repository
uses: actions/checkout@v4

# actions/cache will detect zstd and will become much faster.
- name: Install zstd
run: |
apt-get update -y
apt-get install -y zstd

- name: Restore .build
if: ${{ github.run_attempt == 1 }} # Because maybe the cache is causing issues
id: "restore-cache"
uses: actions/cache/restore@v4
with:
path: .build
key: "test-${{ runner.os }}-${{ github.event.pull_request.base.sha || github.event.after }}"
restore-keys: "test-${{ runner.os }}-"

- name: Prebuild Application
run: |
apt-get -q update \
&& apt-get -q dist-upgrade -y \
&& apt-get install -y libjemalloc-dev \
&& apt-get install -y libssl-dev \
&& rm -rf /var/lib/apt/lists/*

swift build --build-tests --enable-code-coverage --force-resolved-versions

- name: Cache .build
if: steps.restore-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: .build
key: "test-${{ runner.os }}-${{ github.event.pull_request.base.sha || github.event.after }}"

- name: Run unit tests
run: swift test --skip-build --enable-code-coverage --parallel
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ DerivedData/
Tests/LinuxMain.swift
.vscode
.swiftpm/xcode/
.env.*
.env.*
!.env.testing
2 changes: 1 addition & 1 deletion .swift-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.9
6.0
13 changes: 11 additions & 2 deletions .swiftformat
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
--patternlet inline
--swiftversion 5.6
--trimwhitespace nonblank-lines
--ifdef no-indent

Expand All @@ -9,11 +8,21 @@
--disable redundantReturn

--enable isEmpty
--enable sortedSwitchCases
--enable sortSwitchCases
--enable wrapEnumCases
--enable wrapSwitchCases
--enable wrapConditionalBodies
--enable blankLinesBetweenImports
--enable blankLineAfterSwitchCase
--enable blankLinesBetweenImports
--enable blockComments
--enable emptyExtensions
--enable preferSwiftTesting
--enable singlePropertyPerLine
--enable redundantMemberwiseInit
--enable redundantAsync
--enable redundantEquatable
--enable redundantProperty
--enable redundantThrows

--header ""
33 changes: 16 additions & 17 deletions Sources/App/Context/HomeContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ struct HomeContext: Content {
var regularDropInSessions: [DropInSession] = []
var groupDropInSessions: [DropInSession] = []
var schedule: [ScheduleDay] = []
var phase: PhaseContext? = nil
var event: EventContext? = nil
var phase: PhaseContext?
var event: EventContext?
}

struct EventContext: Codable {
Expand All @@ -37,33 +37,32 @@ struct EventContext: Codable {
let isHidden: Bool

init(event: Event) {
self.name = event.name
name = event.name
self.event = event.name.components(separatedBy: " ").first ?? "SwiftLeeds"
self.year = event.name.components(separatedBy: " ").last ?? ""
year = event.name.components(separatedBy: " ").last ?? ""

// This is a total hack, but means if we set the date of an event to something earlier than 2015 then the event is hidden.
// This prevents people accessing that year, useful for development and preparing.
// TODO: just make event.date optional in database
let isKnownDate = event.date.timeIntervalSince1970 > 1420074000

self.date = isKnownDate ? event.date : nil
self.dateFormatted = isKnownDate ? Self.buildConferenceDateString(for: event) : nil
date = isKnownDate ? event.date : nil
dateFormatted = isKnownDate ? Self.buildConferenceDateString(for: event) : nil

self.location = event.location
location = event.location

self.isCurrent = event.isCurrent
self.isFuture = event.date > Date() && !isKnownDate
self.isPast = event.date <= Date() && isKnownDate
self.isHidden = isKnownDate != true
isCurrent = event.isCurrent
isFuture = event.date > Date() && !isKnownDate
isPast = event.date <= Date() && isKnownDate
isHidden = isKnownDate != true
}

private static func buildConferenceDateString(for event: Event) -> String? {
let date = event.date

let days = event.days
.filter { $0.name.contains("Talkshow") == false }
.count

.count(where: { $0.name.contains("Talkshow") == false })

let formatter = DateFormatter()
formatter.locale = .init(identifier: "en_US_POSIX")

Expand Down Expand Up @@ -107,8 +106,8 @@ struct CfpContext: Content {

let stage: Stage
var faqs: [Question] = []
var phase: PhaseContext? = nil
var event: EventContext? = nil
var phase: PhaseContext?
var event: EventContext?
}

struct TeamContext: Content {
Expand All @@ -122,5 +121,5 @@ struct TeamContext: Content {
}

var teamMembers: [TeamMember] = []
var event: EventContext? = nil
var event: EventContext?
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ struct ActivityRouteController: RouteCollection {
}

@Sendable private func onDelete(request: Request) async throws -> Response {
guard let activity = try await Activity.find(request.parameters.get("id"), on: request.db) else { throw Abort(.notFound) }
guard let activity = try await Activity.find(request.parameters.get("id"), on: request.db) else {
throw Abort(.notFound)
}
try await activity.delete(on: request.db)
return Response(status: .ok, body: .init(string: "OK"))
}
Expand Down Expand Up @@ -62,7 +64,7 @@ struct ActivityRouteController: RouteCollection {
return event
}()

if let activity = activity {
if let activity {
var fileName = activity.image

if !image.activityImage.filename.isEmpty {
Expand Down Expand Up @@ -116,12 +118,14 @@ struct ActivityRouteController: RouteCollection {
}

// MARK: - ActivityContext

private struct ActivityContext: Content {
let activity: Activity?
let events: [Event]
}

// MARK: - FormInput

private struct FormInput: Content {
let eventID: String
let title: String
Expand All @@ -132,6 +136,7 @@ struct ActivityRouteController: RouteCollection {
}

// MARK: - ImageUpload

struct ImageUpload: Content {
var activityImage: File
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation

enum ActivityTransformer: Transformer {
static func transform(_ entity: Activity?) -> ActivityResponse? {
guard let entity = entity, let id = entity.id else {
guard let entity, let id = entity.id else {
return nil
}

Expand Down
7 changes: 0 additions & 7 deletions Sources/App/Features/Auth/Models/Permissions.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
//
// Permissions.swift
// swift-leeds
//
// Created by James Sherlock on 12/10/2025.
//

import Vapor

enum Permission: String, CaseIterable {
Expand Down
6 changes: 3 additions & 3 deletions Sources/App/Features/Auth/Models/User.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,19 @@ final class User: Authenticatable, ModelAuthenticatable, Content, ModelSessionAu
)
}

public static func credentialsAuthenticator(
static func credentialsAuthenticator(
database _: DatabaseID? = nil
) -> any Authenticator {
return CustomCredentialsAuthenticator()
}

public static func sessionAuthenticator(
static func sessionAuthenticator(
_: DatabaseID? = nil
) -> any Authenticator {
return SessionAuthenticator()
}

public static func authenticator(
static func authenticator(
database _: DatabaseID? = nil
) -> any Authenticator {
return BearerAuthenticatable()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,12 @@ struct DropInRouteController: RouteCollection {
.filter(\.$id == id)
.with(\.$slots)
.first()
})?.get() else { throw Abort(.notFound) }
})?.get() else {
throw Abort(.notFound)
}

await withThrowingTaskGroup(of: Void.self) { group in
session.slots.forEach { slot in
for slot in session.slots {
group.addTask {
try await slot.delete(on: request.db)
}
Expand Down Expand Up @@ -99,7 +101,7 @@ struct DropInRouteController: RouteCollection {
return (tuple.0.0, date, duration)
}

if Set(slotValues.map { $0.date }).count != slotValues.count {
if Set(slotValues.map(\.date)).count != slotValues.count {
throw Abort(.badRequest, reason: "Duplicate Slots at Same Time")
}

Expand All @@ -109,7 +111,8 @@ struct DropInRouteController: RouteCollection {
// If a slot ID is provided, then query that. Otherwise, create a new slot.
guard let slot = slotTuple.id == "" ?
DropInSessionSlot() :
try await DropInSessionSlot.find(UUID(uuidString: slotTuple.id), on: request.db).get() else {
try await DropInSessionSlot.find(UUID(uuidString: slotTuple.id), on: request.db).get()
else {
throw Abort(.badRequest, reason: "Failed to find existing slot")
}

Expand Down Expand Up @@ -212,27 +215,32 @@ struct DropInRouteController: RouteCollection {
}

func uploadAndReturnImage(_ image: File?) async throws -> String? {
guard let image = image, image.filename != "" else { return nil }
guard let image, image.filename != "" else {
return nil
}

let fileName = "\(UUID.generateRandom().uuidString)-\(image.filename)"
try await ImageService.uploadFile(data: Data(image.data.readableBytesView), filename: fileName)
return fileName
}

// MARK: - SessionContext

private struct SessionContext: Content {
let session: DropInSession?
let events: [Event]
let slots: [DropInSessionSlot]
}

// MARK: - ImageInput

private struct ImageInput: Content {
let ownerImage: File?
let companyImage: File?
}

// MARK: - FormInput

private struct FormInput: Content {
let title: String
let description: String
Expand Down Expand Up @@ -287,7 +295,7 @@ struct DropInRouteController: RouteCollection {
.sorted(by: { $0.key < $1.key })
// enumerate them to turn into conference day number
.enumerated()
.map { (offset, result) in
.map { offset, result in
let eventDay = session.event.days.first(where: { $0.date.withoutTime == result.value[0].date.withoutTime })
return DropInSessionSlotsContext.SlotGroup(title: eventDay?.name ?? "Day \(offset + 1)", slots: result.value)
}
Expand All @@ -304,10 +312,10 @@ struct DropInSessionViewModel: Codable {
let owner: String

init(model: DropInSession) {
self.id = model.id?.uuidString ?? ""
self.title = model.title
self.description = model.description
self.owner = model.owner
id = model.id?.uuidString ?? ""
title = model.title
description = model.description
owner = model.owner
}
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/App/Features/DropIns/Models/DropInSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ final class DropInSession: Model, Content, @unchecked Sendable {
var isPublic: Bool

@Parent(key: "event_id")
public var event: Event
var event: Event

@Children(for: \.$session)
var slots: [DropInSessionSlot]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ final class DropInSessionSlot: Model, Content, @unchecked Sendable {
var duration: Int // in minutes

@Parent(key: "session_id")
public var session: DropInSession
var session: DropInSession

init() {}
}
Loading