-
Notifications
You must be signed in to change notification settings - Fork 0
Support open deployments and related realtime events #175
Conversation
If the user is not logged in, they don't have a teamId. Unless there's something that I'm missing, the path can't have |
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.
A few comments.
src/realtime/realtime-hapi-plugin.ts
Outdated
private requestHandlerFactory(streamFactory: StreamFactory) { | ||
return async (request: Hapi.Request, reply: Hapi.IReply) => { | ||
try { | ||
// const observable = await this.getObservable(teamId, since ? parseInt(since, 10) : undefined); |
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.
Comment
src/realtime/realtime-hapi-plugin.ts
Outdated
@@ -304,7 +324,7 @@ export class RealtimeHapiPlugin { | |||
try { | |||
const deployment = event.payload.deployment; | |||
const apiResponse = await this.jsonApiPlugin.getEntity( | |||
'deployment', api => api.toApiDeployment(deployment.projectId, deployment)); | |||
'deployment', api => api.toApiDeployment(deployment.projectId, deployment)); |
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.
The last )
should be on the following line.
src/realtime/realtime-hapi-plugin.ts
Outdated
super({ | ||
name: 'realtime-plugin', | ||
version: '1.0.0', | ||
}); | ||
this.persistedEvents = this.eventBus.getStream() | ||
.filter(isPersistedEvent) | ||
.map(event => <PersistedEvent<any>> event) |
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.
Since isPersistedEvent
is a type guard, isn't this map
unnecessary?
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.
It's purpose is only to make the compiler agree that this.persistedEvents
has the desired narrowed type.
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 tested it without the map and that seemed to be the case.
src/realtime/realtime-hapi-plugin.ts
Outdated
} | ||
observable = Observable.concat(Observable.from(existing), observable); | ||
private async getStream(request: Hapi.Request) { | ||
const {teamId, projectId, deploymentId} = request.params; |
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 have spaces inside curly brackets.
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.
Are we missing some linter rule?
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.
It's currently not a supported rule in tslint AFAIK.
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.
src/realtime/realtime-hapi-plugin.ts
Outdated
this.persistedEvents.filter(predicate), | ||
); | ||
if (since && !isNaN(since)) { | ||
const existing = await this.eventBus.getEvents(_teamId, since); |
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.
Will this pass on missed events that are not related to this deployment if projectId
and deploymentId
exist?
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.
A nice catch indeed 👍
63155a4
to
3543baa
Compare
5fad3be
to
e247cf8
Compare
Changed to not require the |
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.
Good idea splitting up the realtime stuff into a separate module.
I haven't finished going over all the code, but one bigger thing comes to mind: in the future, it's likely that we want to configure deployment "openness" on a per-project basis. Because of this, making the API ready for this type of check is likely the best route since it's logically clear and we can easily implement our current situation in the "is this project open?" check.
In the realtime API, you now do some checks related to an "open team ID", but I feel it would be better to have some sort of "is this project open?" check instead. That check can then be implemented as "is this project an open team's project?" in our current situation. That also allows us to have multiple "open" teams with minimal changes in the future, if needed.
} else { | ||
server.route(this.traceRoute()); | ||
server.route(this.rawDeploymentRoutes(false)); | ||
server.route(this.rawDeploymentRoutes(true)); |
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.
Are both of these here on purpose?
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, the one with false
is for the open deployments
}; | ||
} | ||
|
||
private rawDeploymentRoutes(needsAuthorization: boolean): Hapi.IRouteConfiguration[] { |
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.
Much clearer now. 👍
@@ -39,6 +39,7 @@ interface SSE { | |||
|
|||
const flowToken = process.env.FLOWDOCK_FLOW_TOKEN; | |||
const projectFolder = process.env.SYSTEM_TEST_PROJECT ? process.env.SYSTEM_TEST_PROJECT : 'blank'; | |||
const openProjectFolder = process.env.SYSTEM_TEST_PROJECT_OPEN ? process.env.SYSTEM_TEST_PROJECT_OPEN : 'blank-open'; |
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 (and other adjacent rows) could be simplified to
const openProjectFolder = process.env.SYSTEM_TEST_PROJECT_OPEN || 'blank-open';
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.
Done
src/realtime/realtime-module.ts
Outdated
// listens to SSEEvents and shares them | ||
this.sseStream = this.eventBus.getStream() | ||
.filter(isPersistedEvent) | ||
.map(event => event as PersistedEvent<any>) |
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.
As mentioned, it seems that removing the map still makes this.sseStream
of the type Observable<PersistedEvent<any>>
because of the type guard, making it unnecessary.
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.
Right. I'd like to say the typings for filter
have improved, making the map unnecessary.
src/realtime/realtime-hapi-plugin.ts
Outdated
cors: true, | ||
validate: { | ||
params: { | ||
teamId: Joi.number().required(), |
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 this be here?
src/realtime/realtime-hapi-plugin.ts
Outdated
this.logger.error(msg, { error, event }); | ||
throw Error(msg); | ||
const { projectId, deploymentId } = request.params; | ||
const teamId = await request.getOpenTeamId(); |
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.
Doesn't this now assume that whenever we fetch events from this endpoint that they belong to the open team? This is not the case if I'm a logged in user from another team and I use a Deployment View as a landing page, at which point this endpoint will be used.
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.
A higher-level comment about this handler in the main review comment.
src/server/hapi.ts
Outdated
@@ -20,6 +20,8 @@ declare module 'hapi' { | |||
interface RequestDecorators { | |||
userHasAccessToProject: (projectId: number) => Promise<boolean>; | |||
userHasAccessToTeam: (teamId: number) => Promise<boolean>; | |||
isOpenDeployment: (projectId: number, deploymentId: number) => Promise<boolean>; | |||
getOpenTeamId: () => Promise<number | undefined>; |
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 if we have multiple teams that want open deployments?
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'd maybe get rid of this function altogether. More info in the main review comment.
Good points. The initial implementation left much to be desired. I've now rehauled the way the 'open' check works and feel this is much closer to what it should've been. |
36649b6
to
4e7d6db
Compare
return async ( | ||
payload: AccessToken, | ||
request: Hapi.Request, | ||
callback: (err: any, valid: boolean, credentials?: any) => void, | ||
) => { | ||
let isAuthorized = false; | ||
let isAuthorized: boolean | undefined = 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.
Since this can now have three values, I'd prefer e.g. an enum to make it explicit what each state means.
Refactor DeploymentHapiPlugin to be clearer
Extract a separate module from RealtimeHapiPlugin
Also make it more clear when the request has been authenticated, but not yet authorized
Also make better use of subclassing in the private version
4e7d6db
to
3aa3188
Compare
Add typings and include authorizationStatus as an enum
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.
Very nice changes! Two very minor things.
// Store the original hostname | ||
request.app.hostname = hostname; | ||
// Overwrite the hostname to use virtual host routing | ||
// Each vhost (including the default one) has a separate routing table |
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.
Is this still relevant?
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, we still use two separate ones
return callback( | ||
undefined, | ||
authorizationStatus === AuthorizationStatus.AUTHORIZED || | ||
authorizationStatus === AuthorizationStatus.NOT_CHECKED, |
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 be indented a bit more.
🎉 |
If a raw deployment is considered 'open', it's served without authentication or authorization.
Adds a new endpoint at
/events/deployment/{projectId}-{deploymentId}
which returnsCOMMENT_ADDED
andCOMMENT_DELETED
events related to the specified open deployment.NOTE: change the base to master after #174 has been merged.