Skip to content

Commit

Permalink
Support tags with spaces etc
Browse files Browse the repository at this point in the history
  • Loading branch information
0xTim committed Jan 18, 2020
1 parent b29b7b5 commit 5292b62
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 66 deletions.
18 changes: 9 additions & 9 deletions Sources/SteamPress/Controllers/Admin/PostAdminController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ struct PostAdminController: RouteCollection {

var existingTagsQuery = [EventLoopFuture<BlogTag?>]()
for tagName in data.tags {
try existingTagsQuery.append(tagsRepository.getTag(BlogTag.percentEncodedTagName(from: tagName), on: req))
existingTagsQuery.append(tagsRepository.getTag(tagName, on: req))
}

return existingTagsQuery.flatten(on: req).flatMap { existingTagsWithOptionals in
let existingTags = existingTagsWithOptionals.compactMap { $0 }
var tagsSaves = [EventLoopFuture<BlogTag>]()
for tagName in data.tags {
if try !existingTags.contains { try $0.name == BlogTag.percentEncodedTagName(from: tagName) } {
let tag = try BlogTag(name: tagName)
if !existingTags.contains { $0.name == tagName } {
let tag = BlogTag(name: tagName)
tagsSaves.append(tagsRepository.save(tag, on: req))
}
}
Expand Down Expand Up @@ -137,9 +137,9 @@ struct PostAdminController: RouteCollection {

let tagsRepository = try req.make(BlogTagRepository.self)
return tagsRepository.getTags(for: post, on: req).flatMap { existingTags in
let tagsToUnlink = try existingTags.filter { (anExistingTag) -> Bool in
let tagsToUnlink = existingTags.filter { (anExistingTag) -> Bool in
for tagName in data.tags {
if try anExistingTag.name == BlogTag.percentEncodedTagName(from: tagName) {
if anExistingTag.name == tagName {
return false
}
}
Expand All @@ -150,15 +150,15 @@ struct PostAdminController: RouteCollection {
removeTagLinkResults.append(tagsRepository.remove(tagToUnlink, from: post, on: req))
}

let newTagsNames = try data.tags.filter { (tagName) -> Bool in
try !existingTags.contains { (existingTag) -> Bool in
try existingTag.name == BlogTag.percentEncodedTagName(from: tagName)
let newTagsNames = data.tags.filter { (tagName) -> Bool in
!existingTags.contains { (existingTag) -> Bool in
existingTag.name == tagName
}
}

var tagCreateSaves = [EventLoopFuture<BlogTag>]()
for newTagName in newTagsNames {
let newTag = try BlogTag(name: newTagName)
let newTag = BlogTag(name: newTagName)
tagCreateSaves.append(tagsRepository.save(newTag, on: req))
}

Expand Down
13 changes: 2 additions & 11 deletions Sources/SteamPress/Models/BlogTag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,10 @@ public final class BlogTag: Codable {
public var tagID: Int?
public var name: String

public init(id: Int? = nil, name: String) throws {
public init(id: Int? = nil, name: String) {
self.tagID = id
self.name = try BlogTag.percentEncodedTagName(from: name)
self.name = name
}
}

extension BlogTag: Content {}

extension BlogTag {
static func percentEncodedTagName(from name: String) throws -> String {
guard let percentEncodedName = name.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
throw SteamPressError(identifier: "BlogTag", "Unable to create tag from name \(name)")
}
return percentEncodedName
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
struct BlogIndexPageContext: Encodable {
let posts: [ViewBlogPost]
let tags: [BlogTag]
let tags: [ViewBlogTag]
let authors: [BlogUser]
let pageInformation: BlogGlobalPageInformation
let title = "Blog"
Expand Down
12 changes: 2 additions & 10 deletions Sources/SteamPress/Models/Contexts/ContextViews/ViewBlogPost.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,9 @@ extension BlogPost {
func toViewPost(authorName: String, authorUsername: String, longFormatter: LongPostDateFormatter, numericFormatter: NumericPostDateFormatter, tags: [BlogTag]) throws -> ViewBlogPost {
let viewPost = try self.toViewPostWithoutTags(authorName: authorName, authorUsername: authorUsername, longFormatter: longFormatter, numericFormatter: numericFormatter)

let viewTags = try tags.map { tag -> ViewBlogTag in
guard let urlEncodedName = tag.name.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
throw SteamPressError(identifier: "ViewBlogPost", "Failed to URL encode tag name")
}
guard let tagID = tag.tagID else {
throw SteamPressError(identifier: "ViewBlogPost", "Tag has no ID")
}
return ViewBlogTag(tagID: tagID, name: tag.name, urlEncodedName: urlEncodedName)
}
let viewTags = try tags.map { try $0.toViewBlogTag() }

return try ViewBlogPost(blogID: viewPost.blogID, title: viewPost.title, contents: viewPost.contents, author: viewPost.author, created: viewPost.created, lastEdited: viewPost.lastEdited, slugUrl: viewPost.slugUrl, published: viewPost.published, longSnippet: viewPost.longSnippet, createdDateLong: viewPost.createdDateLong, createdDateNumeric: viewPost.createdDateNumeric, lastEditedDateNumeric: viewPost.lastEditedDateNumeric, lastEditedDateLong: viewPost.lastEditedDateLong, authorName: viewPost.authorName, authorUsername: viewPost.authorUsername, postImage: viewPost.postImage, postImageAlt: viewPost.postImageAlt, description: viewPost.description, tags: viewTags)
return ViewBlogPost(blogID: viewPost.blogID, title: viewPost.title, contents: viewPost.contents, author: viewPost.author, created: viewPost.created, lastEdited: viewPost.lastEdited, slugUrl: viewPost.slugUrl, published: viewPost.published, longSnippet: viewPost.longSnippet, createdDateLong: viewPost.createdDateLong, createdDateNumeric: viewPost.createdDateNumeric, lastEditedDateNumeric: viewPost.lastEditedDateNumeric, lastEditedDateLong: viewPost.lastEditedDateLong, authorName: viewPost.authorName, authorUsername: viewPost.authorUsername, postImage: viewPost.postImage, postImageAlt: viewPost.postImageAlt, description: viewPost.description, tags: viewTags)
}
}

Expand Down
22 changes: 22 additions & 0 deletions Sources/SteamPress/Models/Contexts/ContextViews/ViewBlogTag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,25 @@ struct ViewBlogTag: Encodable {
let name: String
let urlEncodedName: String
}

extension BlogTag {
func toViewBlogTag() throws -> ViewBlogTag {
guard let urlEncodedName = self.name.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
throw SteamPressError(identifier: "ViewBlogPost", "Failed to URL encode tag name")
}
guard let tagID = self.tagID else {
throw SteamPressError(identifier: "ViewBlogPost", "Tag has no ID")
}
return ViewBlogTag(tagID: tagID, name: self.name, urlEncodedName: urlEncodedName)
}
}

extension ViewBlogTag {

static func percentEncodedTagName(from name: String) throws -> String {
guard let percentEncodedName = name.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
throw SteamPressError(identifier: "BlogTag", "Unable to create tag from name \(name)")
}
return percentEncodedName
}
}
3 changes: 2 additions & 1 deletion Sources/SteamPress/Presenters/ViewBlogPresenter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ public struct ViewBlogPresenter: BlogPresenter {
let viewRenderer = try container.make(ViewRenderer.self)
#warning("Test all the view post stuff")
let viewPosts = try posts.convertToViewBlogPosts(authors: authors, tagsForPosts: tagsForPosts, on: container)
let context = BlogIndexPageContext(posts: viewPosts, tags: tags, authors: authors, pageInformation: pageInformation, paginationTagInformation: paginationTagInfo)
let viewTags = try tags.map { try $0.toViewBlogTag() }
let context = BlogIndexPageContext(posts: viewPosts, tags: viewTags, authors: authors, pageInformation: pageInformation, paginationTagInformation: paginationTagInfo)
return viewRenderer.render("blog/blog", context)
} catch {
return container.future(error: error)
Expand Down
5 changes: 1 addition & 4 deletions Sources/SteamPress/Repositories/SteamPressRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@ extension BlogTag: Parameter {
public typealias ResolvedParameter = EventLoopFuture<BlogTag>
public static func resolveParameter(_ parameter: String, on container: Container) throws -> EventLoopFuture<BlogTag> {
let tagRepository = try container.make(BlogTagRepository.self)
guard let encodedName = parameter.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) else {
throw SteamPressError(identifier: "Invalid-Name", "Unable to convert \(parameter) to URL Encoded String")
}
return tagRepository.getTag(encodedName, on: container).unwrap(or: Abort(.notFound))
return tagRepository.getTag(parameter, on: container).unwrap(or: Abort(.notFound))
}
}
22 changes: 6 additions & 16 deletions Tests/SteamPressTests/BlogTests/TagTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,6 @@ class TagTests: XCTestCase {
XCTAssertEqual(presenter.tagPosts?.first?.title, postData.post.title)
XCTAssertEqual(presenter.tag?.name, tag.name)
}

func testTagNameContainsUrlEncodedName() throws {
let tag = try BlogTag(name: "Luke's Tatooine")
XCTAssertEqual(tag.name, "Luke's%20Tatooine")
}

func testGettingTagViewWithURLEncodedName() throws {
let tagName = "Some Tag"
_ = try testWorld.createTag(tagName)

let urlEncodedName = try XCTUnwrap(tagName.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed))
let response = try testWorld.getResponse(to: "/tags/\(urlEncodedName)")

XCTAssertEqual(response.http.status, .ok)
XCTAssertEqual(presenter.tag?.name.removingPercentEncoding, tagName)
}

func testTagPageGetsCorrectPageInformation() throws {
_ = try testWorld.getResponse(to: tagRequestPath)
Expand All @@ -90,6 +74,12 @@ class TagTests: XCTestCase {
XCTAssertEqual(presenter.tagPageInformation?.websiteURL.absoluteString, "/")
}

func testRequestToURLEncodedTag() throws {
_ = try testWorld.createTag("Some tag")
let response = try testWorld.getResponse(to: "/tags/Some%20tag")
XCTAssertEqual(response.http.status, .ok)
}

func testTagPageInformationGetsLoggedInUser() throws {
_ = try testWorld.getResponse(to: tagRequestPath, loggedInUser: postData.author)
XCTAssertEqual(presenter.tagPageInformation?.loggedInUser?.username, postData.author.username)
Expand Down
2 changes: 1 addition & 1 deletion Tests/SteamPressTests/Helpers/InMemoryRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class InMemoryRepository: BlogTagRepository, BlogPostRepository, BlogUserReposit
}

func addTag(name: String) throws -> BlogTag {
let newTag = try BlogTag(id: tags.count + 1, name: name)
let newTag = BlogTag(id: tags.count + 1, name: name)
tags.append(newTag)
return newTag
}
Expand Down
24 changes: 12 additions & 12 deletions Tests/SteamPressTests/ViewTests/BlogPresenterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class BlogPresenterTests: XCTestCase {
basicContainer.services.register(LongPostDateFormatter.self)
basicContainer.services.register(NumericPostDateFormatter.self)
viewRenderer = CapturingViewRenderer(worker: basicContainer)
testTag = try! BlogTag(id: 1, name: "Tattoine")
testTag = BlogTag(id: 1, name: "Tattoine")
}

override func tearDown() {
Expand All @@ -46,7 +46,7 @@ class BlogPresenterTests: XCTestCase {
// MARK: - All Tags Page

func testParametersAreSetCorrectlyOnAllTagsPage() throws {
let tags = try [BlogTag(id: 0, name: "tag1"), BlogTag(id: 1, name: "tag2")]
let tags = [BlogTag(id: 0, name: "tag1"), BlogTag(id: 1, name: "tag2")]

let pageInformation = buildPageInformation(currentPageURL: allTagsURL)
_ = presenter.allTagsView(on: basicContainer, tags: tags, tagPostCounts: [:], pageInformation: pageInformation)
Expand All @@ -66,8 +66,8 @@ class BlogPresenterTests: XCTestCase {
}

func testTagsPageGetsPassedTagsSortedByPostCount() throws {
let tag1 = try BlogTag(id: 0, name: "Engineering")
let tag2 = try BlogTag(id: 1, name: "Tech")
let tag1 = BlogTag(id: 0, name: "Engineering")
let tag2 = BlogTag(id: 1, name: "Tech")
let tags = [tag1, tag2]
let tagPostCount = [0: 5, 1: 20]
let pageInformation = buildPageInformation(currentPageURL: allTagsURL)
Expand All @@ -81,8 +81,8 @@ class BlogPresenterTests: XCTestCase {
}

func testTagsPageHandlesNoPostsForTagsCorrectly() throws {
let tag1 = try BlogTag(id: 0, name: "Engineering")
let tag2 = try BlogTag(id: 1, name: "Tech")
let tag1 = BlogTag(id: 0, name: "Engineering")
let tag2 = BlogTag(id: 1, name: "Tech")
let tags = [tag1, tag2]
let tagPostCount = [0: 0, 1: 20]
let pageInformation = buildPageInformation(currentPageURL: allTagsURL)
Expand All @@ -94,7 +94,7 @@ class BlogPresenterTests: XCTestCase {
}

func testTwitterHandleNotSetOnAllTagsPageIfNotGiven() throws {
let tags = try [BlogTag(id: 0, name: "tag1"), BlogTag(id: 1, name: "tag2")]
let tags = [BlogTag(id: 0, name: "tag1"), BlogTag(id: 1, name: "tag2")]
let pageInformation = buildPageInformation(currentPageURL: allTagsURL, siteTwitterHandle: nil)
_ = presenter.allTagsView(on: basicContainer, tags: tags, tagPostCounts: [:], pageInformation: pageInformation)

Expand All @@ -103,7 +103,7 @@ class BlogPresenterTests: XCTestCase {
}

func testDisqusNameNotSetOnAllTagsPageIfNotGiven() throws {
let tags = try [BlogTag(id: 0, name: "tag1"), BlogTag(id: 1, name: "tag2")]
let tags = [BlogTag(id: 0, name: "tag1"), BlogTag(id: 1, name: "tag2")]
let pageInformation = buildPageInformation(currentPageURL: allTagsURL, disqusName: nil)
_ = presenter.allTagsView(on: basicContainer, tags: tags, tagPostCounts: [:], pageInformation: pageInformation)

Expand All @@ -112,7 +112,7 @@ class BlogPresenterTests: XCTestCase {
}

func testGAIdentifierNotSetOnAllTagsPageIfNotGiven() throws {
let tags = try [BlogTag(id: 0, name: "tag1"), BlogTag(id: 1, name: "tag2")]
let tags = [BlogTag(id: 0, name: "tag1"), BlogTag(id: 1, name: "tag2")]
let pageInformation = buildPageInformation(currentPageURL: allTagsURL, googleAnalyticsIdentifier: nil)
_ = presenter.allTagsView(on: basicContainer, tags: tags, tagPostCounts: [:], pageInformation: pageInformation)

Expand All @@ -121,7 +121,7 @@ class BlogPresenterTests: XCTestCase {
}

func testLoggedInUserSetOnAllTagsPageIfPassedIn() throws {
let tags = try [BlogTag(id: 0, name: "tag1"), BlogTag(id: 1, name: "tag2")]
let tags = [BlogTag(id: 0, name: "tag1"), BlogTag(id: 1, name: "tag2")]
let user = TestDataBuilder.anyUser()
let pageInformation = buildPageInformation(currentPageURL: allTagsURL, user: user)
_ = presenter.allTagsView(on: basicContainer, tags: tags, tagPostCounts: [:], pageInformation: pageInformation)
Expand Down Expand Up @@ -295,8 +295,8 @@ class BlogPresenterTests: XCTestCase {
post.blogID = 1
let post2 = try TestDataBuilder.anyPost(author: author2, title: "Another Title")
post2.blogID = 2
let tag1 = try BlogTag(id: 1, name: "Engineering")
let tag2 = try BlogTag(id: 2, name: "Fun")
let tag1 = BlogTag(id: 1, name: "Engineering")
let tag2 = BlogTag(id: 2, name: "Fun")
let tags = [tag1, tag2]
let currentPage = 2
let totalPages = 10
Expand Down
14 changes: 13 additions & 1 deletion Tests/SteamPressTests/ViewTests/BlogViewTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class BlogViewTests: XCTestCase {
}

func testBlogPostPageGetsCorrectParameters() throws {
let tag = try BlogTag(id: 1, name: "Engineering")
let tag = BlogTag(id: 1, name: "Engineering")
_ = presenter.postView(on: basicContainer, post: post, author: author, tags: [tag], pageInformation: pageInformation)

let context = try XCTUnwrap(viewRenderer.capturedContext as? BlogPostPageContext)
Expand Down Expand Up @@ -94,5 +94,17 @@ class BlogViewTests: XCTestCase {
let context = try XCTUnwrap(viewRenderer.capturedContext as? BlogPostPageContext)
XCTAssertNil(context.pageInformation.googleAnalyticsIdentifier)
}

func testGettingTagViewWithURLEncodedName() throws {
let tagName = "Some Tag"
let urlEncodedName = try XCTUnwrap(tagName.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed))
let tag = BlogTag(id: 1, name: tagName)

_ = presenter.postView(on: basicContainer, post: post, author: author, tags: [tag], pageInformation: pageInformation)

let context = try XCTUnwrap(viewRenderer.capturedContext as? BlogPostPageContext)
XCTAssertEqual(context.post.tags.first?.urlEncodedName, urlEncodedName)
XCTAssertEqual(context.post.tags.first?.name, tagName)
}

}

0 comments on commit 5292b62

Please sign in to comment.