From 8369895868a8dece613a05be3b9a1f72bdc71f62 Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Tue, 6 Mar 2018 12:07:06 +0000 Subject: [PATCH 01/14] Remove line --- .../App/Controllers/Slack/SlackAuthenticationController.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/App/Controllers/Slack/SlackAuthenticationController.swift b/Sources/App/Controllers/Slack/SlackAuthenticationController.swift index 83ecd90..cc81698 100644 --- a/Sources/App/Controllers/Slack/SlackAuthenticationController.swift +++ b/Sources/App/Controllers/Slack/SlackAuthenticationController.swift @@ -54,4 +54,3 @@ final class SlackAuthenticationController { } } - From 1785105d8ef2c7a09f6aa3c4fa2659453158b558 Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Tue, 6 Mar 2018 14:21:02 +0000 Subject: [PATCH 02/14] Updated readme file --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d3efbb0..925f963 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,20 @@ -Post & create [Marvel](https://marvelapp.com) projects directly from Slack, written in Swift (Vapor). +BotBot is an open-source Slackbot for [Marvel](https://marvelapp.com) - a design collaboration platform that brings ideas to life. -Build your own integration using the [Marvel API](https://marvelapp.com/developers/). +[GO TO BOTBOT AND INSTALL](https://botbot.marvelapp.com) -[Try it out](https://botbot.marvelapp.com) +BotBot allows you and your team to create, view and manage Marvel projects directly inside of Slack. + +Why it's so amazing +- Anyone in your team can quickly pull up a list of Marvel projects without leaving Slack by typing /projects +- Create a project in seconds by typing /create +- Or just grab the code and roll your own bot + +Built using the [Marvel GraphQL API](https://marvelapp.com/developers/) - get started here. + +Questions? Hit us up on [Twitter](http://twitter.com/marvelapp) ## 🎒 Before building (dependencies) * Install [Xcode](https://developer.apple.com/xcode/) @@ -27,9 +36,3 @@ Build your own integration using the [Marvel API](https://marvelapp.com/develope ## 📖 Documentation Visit the Vapor web framework's [documentation](http://docs.vapor.codes) for instructions on how to use this package. - -## 💧 Community -Join the welcoming community of fellow Vapor developers in [Slack](http://vapor.team). - -## 🔧 Compatibility -This package has been tested on macOS and Heroku. From b5ee082ccdd20dcb8d75e5c0f2d8b34260ba6915 Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Tue, 6 Mar 2018 14:25:01 +0000 Subject: [PATCH 03/14] Cleaner Readme --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 925f963..ee2198a 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,16 @@ BotBot is an open-source Slackbot for [Marvel](https://marvelapp.com) - a design collaboration platform that brings ideas to life. -[GO TO BOTBOT AND INSTALL](https://botbot.marvelapp.com) +[Go to BotBot and install](https://botbot.marvelapp.com) BotBot allows you and your team to create, view and manage Marvel projects directly inside of Slack. -Why it's so amazing -- Anyone in your team can quickly pull up a list of Marvel projects without leaving Slack by typing /projects -- Create a project in seconds by typing /create -- Or just grab the code and roll your own bot +*Why it's so amazing* +* Anyone in your team can quickly pull up a list of Marvel projects without leaving Slack by typing /projects +* Create a project in seconds by typing /create-project +* Or just grab the code and roll your own bot -Built using the [Marvel GraphQL API](https://marvelapp.com/developers/) - get started here. +Built using the Marvel GraphQL API - [get started here]([Marvel GraphQL API](https://marvelapp.com/developers/)). Questions? Hit us up on [Twitter](http://twitter.com/marvelapp) From 1ce0281f199b352af64e14cfd906eea7fa85dcb0 Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Tue, 6 Mar 2018 14:25:43 +0000 Subject: [PATCH 04/14] Typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ee2198a..fb903b5 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,12 @@ BotBot is an open-source Slackbot for [Marvel](https://marvelapp.com) - a design BotBot allows you and your team to create, view and manage Marvel projects directly inside of Slack. -*Why it's so amazing* +**Why it's so amazing** * Anyone in your team can quickly pull up a list of Marvel projects without leaving Slack by typing /projects * Create a project in seconds by typing /create-project * Or just grab the code and roll your own bot -Built using the Marvel GraphQL API - [get started here]([Marvel GraphQL API](https://marvelapp.com/developers/)). +Built using the Marvel GraphQL API - [get started here]([Marvel GraphQL API](https://marvelapp.com/developers/). Questions? Hit us up on [Twitter](http://twitter.com/marvelapp) From b919c9b9ec02a7d8d029d132f47906df6eb48509 Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Tue, 6 Mar 2018 14:27:22 +0000 Subject: [PATCH 05/14] People command added --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fb903b5..eaf1c03 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,9 @@ BotBot is an open-source Slackbot for [Marvel](https://marvelapp.com) - a design BotBot allows you and your team to create, view and manage Marvel projects directly inside of Slack. **Why it's so amazing** -* Anyone in your team can quickly pull up a list of Marvel projects without leaving Slack by typing /projects -* Create a project in seconds by typing /create-project +* Anyone in your team can quickly pull up a list of Marvel projects without leaving Slack by typing ```/projects``` +* Create a project in seconds by typing ```/create-project``` +* Add people to projects by typing ```/add-people``` * Or just grab the code and roll your own bot Built using the Marvel GraphQL API - [get started here]([Marvel GraphQL API](https://marvelapp.com/developers/). From 229c64b36b630bd84e6f84261931c750971480d4 Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Tue, 6 Mar 2018 14:27:57 +0000 Subject: [PATCH 06/14] =?UTF-8?q?Another=20typo=20=F0=9F=A4=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eaf1c03..fc94796 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ BotBot allows you and your team to create, view and manage Marvel projects direc * Add people to projects by typing ```/add-people``` * Or just grab the code and roll your own bot -Built using the Marvel GraphQL API - [get started here]([Marvel GraphQL API](https://marvelapp.com/developers/). +Built using the Marvel GraphQL API - [get started here](https://marvelapp.com/developers/). Questions? Hit us up on [Twitter](http://twitter.com/marvelapp) From 99b0e0bbf72cc829b1f3ab6f9dac5d32f89cb10e Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Tue, 6 Mar 2018 14:31:21 +0000 Subject: [PATCH 07/14] Update Github url --- Resources/Views/home.leaf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Views/home.leaf b/Resources/Views/home.leaf index e7596fa..8765f05 100644 --- a/Resources/Views/home.leaf +++ b/Resources/Views/home.leaf @@ -11,7 +11,7 @@
- +
From 1dbc35be3184eae0b6836a4dac3f13321f2ccef6 Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Tue, 6 Mar 2018 14:35:05 +0000 Subject: [PATCH 08/14] Change copy on the home page --- Resources/Views/home.leaf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Views/home.leaf b/Resources/Views/home.leaf index 8765f05..05fd9ad 100644 --- a/Resources/Views/home.leaf +++ b/Resources/Views/home.leaf @@ -24,7 +24,7 @@

A Slack bot for Marvel

-

Post & create Marvel projects directly from Slack

+

Create and view Marvel projects directly in Slack

Connect with
@@ -58,7 +58,7 @@
From 522f8f34c82eaab9d998a6a231493be52d5c6ba8 Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Tue, 6 Mar 2018 17:05:44 +0000 Subject: [PATCH 09/14] Update License file --- license | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/license b/license index 97523ae..f7cd197 100644 --- a/license +++ b/license @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2017 Qutheory, LLC +Copyright (c) 2017 Marvel Prototyping, Limited Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 9f69a9cfb24539ae6dc781f2bcf33f25acdd6d2f Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Tue, 6 Mar 2018 17:07:05 +0000 Subject: [PATCH 10/14] =?UTF-8?q?2000=20and=20kaboom=2018=20=F0=9F=A4=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- license | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/license b/license index f7cd197..88bc4f9 100644 --- a/license +++ b/license @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2017 Marvel Prototyping, Limited +Copyright (c) 2018 Marvel Prototyping, Limited Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 562d5834827afa99104eda5958c43cbb1fc4a98d Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Tue, 20 Mar 2018 11:08:42 +0000 Subject: [PATCH 11/14] Make robot smaller for small screens --- Public/styles/style.css | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Public/styles/style.css b/Public/styles/style.css index dd17137..3a757a3 100644 --- a/Public/styles/style.css +++ b/Public/styles/style.css @@ -134,6 +134,8 @@ a{ flex-direction: row; align-items: stretch; height: 100vh; + min-height: 840px; + } #home .container > div { @@ -377,3 +379,16 @@ only screen and (min-device-pixel-ratio: 1.5) { background-repeat: no-repeat; } } + + +@media (max-height: 900px) { + + #home .main .robot-area .robot{ + background-size: 258px 223px; + background-repeat: no-repeat; + width: 258px; + height: 223px; + margin-left: -155px; + } + +} From 17e9c870f64fd31a5ca67cb3f05c9f4b8602411f Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Thu, 5 Apr 2018 14:45:45 +0100 Subject: [PATCH 12/14] Needs testing --- .../Slack/SlackExternalController.swift | 18 ++- .../Slack/SlackProjectsController.swift | 31 ++--- Sources/App/Extensions/Array.swift | 28 ++++ Sources/App/Tools/Marvel/GraphQueries.swift | 130 ++++++++++++++---- Sources/App/Tools/Marvel/Marvel.swift | 6 +- 5 files changed, 160 insertions(+), 53 deletions(-) create mode 100644 Sources/App/Extensions/Array.swift diff --git a/Sources/App/Controllers/Slack/SlackExternalController.swift b/Sources/App/Controllers/Slack/SlackExternalController.swift index 7c00038..799fe01 100644 --- a/Sources/App/Controllers/Slack/SlackExternalController.swift +++ b/Sources/App/Controllers/Slack/SlackExternalController.swift @@ -76,8 +76,6 @@ final class SlackExternalController { return try Error.with(name: .noMarvelToken) } - // Get company members - let projectsJSON = try projectsInSlackFormat(request: request, marvelAccessToken: marvelToken) // Filter on what the user is searching for in the field on Slack @@ -155,22 +153,32 @@ final class SlackExternalController { func projectsInSlackFormat(request: Request, marvelAccessToken: String, displayProperty: String = "text") throws -> [JSON] { - let result = try Marvel(droplet: drop).projects(accessToken: marvelAccessToken) + let result = try Marvel(droplet: drop).projectsIncludingCompany(accessToken: marvelAccessToken) - guard let projectsArray = result.data["data"]?["user"]?["projects"]?["edges"]?.array else { + guard var projectsArray = result.data["data"]?["user"]?["projects"]?["edges"]?.array else { throw Abort.badRequest } + // Check if users has company projects + if let companyProjectsArray = result.data["data"]?["user"]?["company"]?["projects"]?["edges"]?.array { + projectsArray.append(contentsOf: companyProjectsArray) + } + var projectsNode = [MarvelProject]() for project in projectsArray{ let project = MarvelProject(with: project["node"]) projectsNode.append(project) } + // Clear duplicates as company projects might include personal projects... + let filteredProjectsNode = projectsNode.filterDuplicates { + $0.pk == $1.pk + + } // Map to a readable Slack format - let projectsJSON = try projectsNode.map { (project) -> JSON in + let projectsJSON = try filteredProjectsNode.map { (project) -> JSON in let json = try JSON(node: [ displayProperty: project.name, diff --git a/Sources/App/Controllers/Slack/SlackProjectsController.swift b/Sources/App/Controllers/Slack/SlackProjectsController.swift index f60e568..44e4938 100644 --- a/Sources/App/Controllers/Slack/SlackProjectsController.swift +++ b/Sources/App/Controllers/Slack/SlackProjectsController.swift @@ -74,30 +74,19 @@ final class SlackProjectsController { return try Error.with(name: .noMarvelToken) } - let projects = try Marvel(droplet: drop).projects(accessToken: marvelToken) - guard let projectsArray = projects.data["data"]?["user"]?["projects"]?["edges"]?.array else { - return try Error.with(name: .general) - } - - var projectsNode = [MarvelProject]() - for project in projectsArray{ - let project = MarvelProject(with: project["node"]) - projectsNode.append(project) - } - - let project = projectsNode.filter { (project) -> Bool in - return project.pk == value - }.first + let projects = try Marvel(droplet: drop).project(pk: value, accessToken: marvelToken) - guard let projectFound = project else { + guard let projectDic = projects.data["data"]?["project"] else { return try Error.with(name: .general) } - let collaborators = projectFound.collaborators.flatMap { (collab) -> String? in + let project = MarvelProject(with: projectDic) + + let collaborators = project.collaborators.flatMap { (collab) -> String? in return collab.username }.joined(separator: ", ") - let collabWord = projectFound.collaborators.count > 1 ? "👧 \(projectFound.collaborators.count) collaborators" : "👧 1 collaborator" + let collabWord = project.collaborators.count > 1 ? "👧 \(project.collaborators.count) collaborators" : "👧 1 collaborator" return try JSON(node: [ "response_type": "in_channel", @@ -105,14 +94,14 @@ final class SlackProjectsController { "delete_original": true, "attachments": [ [ - "title": projectFound.name, - "title_link": projectFound.prototypeUrl, - "thumb_url": projectFound.screens.first?.content?.url ?? "", + "title": project.name, + "title_link": project.prototypeUrl, + "thumb_url": project.screens.first?.content?.url ?? "", "footer": "Marvel Prototyping", "fields": [ [ "title": "⏱ Last updated", - "value": projectFound.lastModified.since().capitalizingFirstLetter(), + "value": project.lastModified.since().capitalizingFirstLetter(), "short": true ], [ diff --git a/Sources/App/Extensions/Array.swift b/Sources/App/Extensions/Array.swift new file mode 100644 index 0000000..d8bf35f --- /dev/null +++ b/Sources/App/Extensions/Array.swift @@ -0,0 +1,28 @@ +// +// Array.swift +// App +// +// Created by Maxime De Greve on 16/02/2018. +// + +import Foundation + +extension Array { + + func filterDuplicates(includeElement: @escaping (_ lhs: Element, _ rhs: Element) -> Bool) -> [Element] { + + var results = [Element]() + + forEach { (element) in + + let existingElements = results.filter { + return includeElement(element, $0) + } + + if existingElements.count == 0 { + results.append(element) + } + } + return results + } +} diff --git a/Sources/App/Tools/Marvel/GraphQueries.swift b/Sources/App/Tools/Marvel/GraphQueries.swift index c98be8e..5241f40 100644 --- a/Sources/App/Tools/Marvel/GraphQueries.swift +++ b/Sources/App/Tools/Marvel/GraphQueries.swift @@ -42,42 +42,76 @@ final class GraphQueries { url } - query { - user { - projects(first: 40) { + { + user { + company { + projects(first: 40) { + edges { + node { + pk + name + prototypeUrl + lastModified + collaborators { + edges { + node { + pk + username + email + } + } + } + screens(first: 2) { + edges { + node { + name + uuid + modifiedAt + content { + __typename + ...image + } + } + } + } + } + } + } + } + projects(first: 40) { + edges { + node { + pk + name + prototypeUrl + lastModified + collaborators { edges { node { pk + username + email + } + } + } + screens(first: 2) { + edges { + node { name - prototypeUrl - lastModified - collaborators { - edges { - node { - pk - username - email - } - } - } - screens(first: 2) { - edges { - node { - name - uuid - modifiedAt - content { - __typename - ... image - } - } - } + uuid + modifiedAt + content { + __typename + ...image } } } } } } + } + } + } """ static func createProject(name: String) -> String{ @@ -96,6 +130,50 @@ final class GraphQueries { } + static func project(pk: Int) -> String{ + + return """ + + fragment image on ImageScreen { + filename + url + } + + query { + project(pk: \(pk) { + pk + name + prototypeUrl + lastModified + collaborators { + edges { + node { + pk + username + email + } + } + } + screens(first: 2) { + edges { + node { + name + uuid + modifiedAt + content { + __typename + ...image + } + } + } + } + } + } + + """ + + } + static func addCollaboratorToProject(email: String, projectPk: Int) -> String{ return """ diff --git a/Sources/App/Tools/Marvel/Marvel.swift b/Sources/App/Tools/Marvel/Marvel.swift index 5b75db2..b87a7d9 100644 --- a/Sources/App/Tools/Marvel/Marvel.swift +++ b/Sources/App/Tools/Marvel/Marvel.swift @@ -96,10 +96,14 @@ final class Marvel { return try graphQL(query: GraphQueries.myUser, accessToken: accessToken) } - func projects(accessToken: String) throws -> Response{ + func projectsIncludingCompany(accessToken: String) throws -> Response{ return try graphQL(query: GraphQueries.projects, accessToken: accessToken) } + func project(pk: Int, accessToken: String) throws -> Response{ + return try graphQL(query: GraphQueries.project(pk: pk), accessToken: accessToken) + } + func createProject(name: String, accessToken: String) throws -> Response{ return try graphQL(query: GraphQueries.createProject(name: name), accessToken: accessToken) } From 06e23b060973436c2714137502f1517c41e648c8 Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Thu, 5 Apr 2018 14:52:18 +0100 Subject: [PATCH 13/14] compactmap --- Sources/App/Controllers/Slack/SlackProjectsController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/App/Controllers/Slack/SlackProjectsController.swift b/Sources/App/Controllers/Slack/SlackProjectsController.swift index 44e4938..95c174a 100644 --- a/Sources/App/Controllers/Slack/SlackProjectsController.swift +++ b/Sources/App/Controllers/Slack/SlackProjectsController.swift @@ -83,7 +83,7 @@ final class SlackProjectsController { let project = MarvelProject(with: projectDic) - let collaborators = project.collaborators.flatMap { (collab) -> String? in + let collaborators = project.collaborators.compactMap { (collab) -> String? in return collab.username }.joined(separator: ", ") let collabWord = project.collaborators.count > 1 ? "👧 \(project.collaborators.count) collaborators" : "👧 1 collaborator" From 20a549e8e79f460420cca002e830c347f9753e0f Mon Sep 17 00:00:00 2001 From: Maxime De Greve Date: Thu, 5 Apr 2018 15:27:52 +0100 Subject: [PATCH 14/14] Fix to post as body not as query --- Sources/App/Tools/Marvel/GraphQueries.swift | 2 +- Sources/App/Tools/Marvel/Marvel.swift | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/App/Tools/Marvel/GraphQueries.swift b/Sources/App/Tools/Marvel/GraphQueries.swift index 5241f40..d73679c 100644 --- a/Sources/App/Tools/Marvel/GraphQueries.swift +++ b/Sources/App/Tools/Marvel/GraphQueries.swift @@ -140,7 +140,7 @@ final class GraphQueries { } query { - project(pk: \(pk) { + project(pk: \(pk)) { pk name prototypeUrl diff --git a/Sources/App/Tools/Marvel/Marvel.swift b/Sources/App/Tools/Marvel/Marvel.swift index b87a7d9..b8b223e 100644 --- a/Sources/App/Tools/Marvel/Marvel.swift +++ b/Sources/App/Tools/Marvel/Marvel.swift @@ -120,15 +120,15 @@ final class Marvel { func graphQL(query: String, accessToken: String) throws -> Response{ - let body: Node = [ + let body: JSON = [ "query": .string(query) ] - let request = Request(method: .post, uri: "\(apiUrl)/graphql/") - request.query = body + request.body = body.makeBody() request.headers = [ - "Authorization" : "Bearer \(accessToken)" + "Authorization" : "Bearer \(accessToken)", + "Content-Type": "application/json" ] return try drop.client.respond(to: request)