-
-
Notifications
You must be signed in to change notification settings - Fork 293
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Project hooks and github status hooks on cypress v5 #148
Conversation
That's 💥 will get it out soon! |
* origin/next: Use eslint Update dependencies, use @babel/node
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is super super great 🚀💥⚡️. Thanks a lot for your great ideas and work!
- I have changed the base for that PR to
next
- I have pushed few changes to
next
to put some focus on dev experience and code quality - eslint, typescript errors and prettier. Now there are more people working on that I want to keep the project clean and avoid broken windows.
As for the code / implementation:
- Let's try to keep
api
anddirector
typed, I didn't do a great job with types on dashboard and have been suffering. Please tell me is that's too much effort, I will try to help - Did you consider a pub/sub for communicating events in director? I am concerned by putting too much logic into routing functions.
- I noticed few opportunities to use optional chaining.
Also, see inline comments
} | ||
|
||
console.log(`>> Machine is joining a run`, { ciBuildId }); | ||
|
||
const response = await app.get('executionDriver').createRun(req.body); | ||
const response = await executionDriver.createRun(req.body); | ||
const runWithSpecs = await executionDriver.getRunWithSpecs(response.runId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think about a pub/sub here?
pub.emit(hookEvents.RUN_START, { runId: response.runId });
The only problem is getting the execution driver, please let me know if you think that is a good option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably personal opinion. In my early days of programming I delt with the event emmiter pattern a lot and grew a major distaste for it. Particularly when stuff goes wrong and your emitter is hooked up to multiple subscribers. Tracking down subscriber order is a massive pain. Also just putting break points and trying to debug into a subscriber is kinda succy. Since event emitters push things to the back of the call stack.
If your not using the built in event system its not as big of a deal. Cause you can follow the code flow a little better. But I generally shy away from it cause I find it more understandable to track direct function calls.
That said if you would like me to change it I still can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it can go left when abused - there are pros and cons. I did see emitters working nicely when controlled and used strictly. I also suffered a lot while working on systems that abused the pattern and lost control of emitters / listeners.
Bottom line, if you dislike emitters so much, let's try to find a different solution. What I am looking for is to separate the functionality that deals with director primary responsibility (manage the tests) from hooks functionality.
Simply extracting all hooks functionality from app.ts
into a separate module would also work for me.
const isRunStillRunning = run.specs.reduce( | ||
( | ||
wasRunning: boolean, | ||
currentSpec: { | ||
claimed: boolean; | ||
results: any; | ||
}, | ||
index: number | ||
) => { | ||
return ( | ||
!currentSpec.claimed || !run.specsFull[index].results || wasRunning | ||
); | ||
}, | ||
false | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be a distinct method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What alternative are you thinking of?
I separated it into a function for readability. If thats what you are asking about.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this
I replied to everything inline.
Thanks! Yeah help on that would be super appreciated. Sorry if im a pain on that. I'm not a big typescript user. Im happy to make whatever changes you think we need to. So if there are places you want me to change types just call them out. Also there seems to be a lot of ways and places to define said types. Would you like them in a single file? Do you want them in the source file that is most closely related to them. Do you want them inline on functions themselfs. Do you want them in a file separate file with the same name as the file that your working on. Do we wanna be sharing them across Guide me please. |
I am not opinionated about that, what is important is consistency. Suggesting to mimic the current director structure. For trivial types inline function would work (example) For more complex types, let's have a For cross-functional shared types let's use root "types" directory. For generated types (from GraphQL) - I am marking the specific functions missing types... |
import { getMongoDB, init } from '@src/lib/mongo'; | ||
import { DataSource } from 'apollo-datasource'; | ||
import uuid from 'uuid/v4'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import uuid from 'uuid/v4'; | |
import uuid from 'uuid/v4'; | |
import { Project } from '@src/duplicatedFromDirector/project.types'; |
@@ -19,6 +21,56 @@ const getSortByAggregation = (direction = 'DESC') => ({ | |||
}, | |||
}); | |||
|
|||
const addHookIdsToProjectHooks = (project) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const addHookIdsToProjectHooks = (project) => { | |
const addHookIdsToProjectHooks = (project: Project) => { |
return project; | ||
}; | ||
|
||
const removeUnusedHookDataFromProject = (project) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const removeUnusedHookDataFromProject = (project) => { | |
const removeUnusedHookDataFromProject = (project: Project) => { |
@@ -27,29 +79,37 @@ export class ProjectsAPI extends DataSource { | |||
async getProjectById(id: string) { | |||
const result = getMongoDB() | |||
.collection('projects') | |||
.aggregate([{ | |||
.aggregate([ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.aggregate([ | |
.aggregate<Project>([ |
hookId: string; | ||
url: string; | ||
headers: string; | ||
hookEvents: [string]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hookEvents: [string]; | |
hookEvents: string[]; |
export interface Project { | ||
projectId: string; | ||
createdAt: string; | ||
hooks?: [Hook]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hooks?: [Hook]; | |
hooks?: Hook[]; |
hookId: string; | ||
url: string; | ||
headers: string; | ||
hookEvents: [string]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hookEvents: [string]; | |
hookEvents: string[]; |
export interface Project { | ||
projectId: string; | ||
createdAt: string; | ||
hooks?: [Hook]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hooks?: [Hook]; | |
hooks?: Hook[]; |
* origin/next: Fine tune eslint and add tsc watch
Sweet. Would you like me to make these extra changes in another PR? |
If you already done some refactoring - sure, otherwise don't waste your time :) |
So this relates to...
#144
#14
#143
And has implications for #120
This introduces a new set of forms on the project edit view that allow you to setup a few types of hooks.
Webhooks: These are pretty straight forward. Every time one of the event hooks are called, json representations of the run and instance involved in the event plus some extra stats data will be sent in a POST call from sorry-cypress to url that is defined. You can also provide headers that will be passed in the post call.
Github Status Hooks: Every time a run starts, finishes or an instance finishes. The provided github repository will receive a status update with the current results and a link to the run. Currently a github personal access token with status access on the repo is required.
github status
This is currently for v5 but I have a non v5 version of the branch as well if we need it.