Skip to content

Commit

Permalink
initial commit of save searches
Browse files Browse the repository at this point in the history
  • Loading branch information
heckj committed Dec 15, 2023
1 parent 212f8c2 commit 2737cf8
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,15 @@
"version" : "1.1.1"
}
},
{
"identity" : "spisearchresult",
"kind" : "remoteSourceControl",
"location" : "https://github.com/heckj/SPISearchResult.git",
"state" : {
"revision" : "29381418b804b43494ab8689e07f31aa66d66349",
"version" : "0.5.1"
}
},
{
"identity" : "sql-kit",
"kind" : "remoteSourceControl",
Expand Down
2 changes: 2 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ let package = Package(
.package(url: "https://github.com/vapor/fluent.git", from: "4.0.0"),
.package(url: "https://github.com/vapor/jwt-kit", from: "4.13.0"),
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
.package(url: "https://github.com/heckj/SPISearchResult.git", from: "0.5.1"),
],
targets: [
.executableTarget(name: "Run", dependencies: ["App"]),
Expand All @@ -70,6 +71,7 @@ let package = Package(
.product(name: "SwiftPMPackageCollections", package: "swift-package-manager"),
.product(name: "Vapor", package: "vapor"),
.product(name: "VaporToOpenAPI", package: "VaporToOpenAPI"),
.product(name: "SPISearchResult", package: "SPISearchResult"),
],
linkerSettings: [.unsafeFlags(["-Xlinker", "-interposable"],
.when(platforms: [.macOS],
Expand Down
1 change: 1 addition & 0 deletions Sources/App/Core/Search.swift
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ enum Search {
return query.all(decoding: DBRecord.self)
.mapEachCompact(Result.init)
.map { results in
SearchLogger.log(query: sanitizedTerms.joined(separator: " "), results: results)
let hasMoreResults = results.filter(\.isPackage).count > pageSize
// first page has non-package results prepended, extend prefix for them
let keep = (page == 1)
Expand Down
102 changes: 102 additions & 0 deletions Sources/App/Core/SearchLogger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright Dave Verwer, Sven A. Schmidt, and other contributors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation
import SPISearchResult

enum SearchLogger {
static var storedSearchesFileURL: URL? {
#if os(macOS)
let local = Foundation.FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("searches", conformingTo: .plainText)
return local
#endif
#if os(Linux)
return URL(fileURLWithPath: "/var/log/searches.txt")
#endif
}

static func log(query: String, results: [Search.Result]) {
var authors: [String] = []
var keywords: [String] = []
var packages: [SearchResult.Package] = []

for result in results {
switch result {
case let .author(authorResult):
authors.append(authorResult.name)
case let .keyword(keywordResult):
keywords.append(keywordResult.keyword)
case let .package(pkgResult):
let pkg = SearchResult.Package(
id: .init(owner: pkgResult.repositoryOwner, repository: pkgResult.repositoryName),
name: pkgResult.packageName,
package_keywords: pkgResult.keywords ?? [],
summary: pkgResult.summary,
stars: pkgResult.stars ?? 0,
has_docs: pkgResult.hasDocs,
last_activity: pkgResult.lastActivityAt
)
packages.append(pkg)
}
}
let combinedResult = SearchResult(timestamp: Date.now, query: query, keywords: keywords, authors: authors, packages: packages)

let jsonEncoder = JSONEncoder()
jsonEncoder.dateEncodingStrategy = .iso8601
jsonEncoder.outputFormatting = .sortedKeys

guard let storedSearchesFileURL else {
return
}
let path = storedSearchesFileURL.path()
if !Foundation.FileManager.default.fileExists(atPath: path) {
// file doesn't exist - create it and write to it
if !Foundation.FileManager.default.createFile(
atPath: path,
contents: Data("# Search Requests\n".utf8), attributes: nil
) {
fatalError("Failed to create file at \(path)")
}
}

let fileHandle: FileHandle?
do {
fileHandle = try FileHandle(forWritingTo: storedSearchesFileURL)
} catch {
// couldn't open file handle
print("Error: \(error.localizedDescription)")
fileHandle = nil
}
guard let fileHandle else {
print("Unable to access fileHandle for \(storedSearchesFileURL)")
return
}
fileHandle.seekToEndOfFile()

let encodedSearchResult: Data?
do {
encodedSearchResult = try jsonEncoder.encode(combinedResult)
} catch {
print("Failed to encode search result: \(error.localizedDescription)")
encodedSearchResult = nil
}
if let encodedSearchResult,
let newlineAsData = "\n".data(using: .utf8)
{
fileHandle.write(encodedSearchResult)
fileHandle.write(newlineAsData)
}
fileHandle.closeFile()
}
}

0 comments on commit 2737cf8

Please sign in to comment.