Input: project_id
Output: db_latency_job_id
-
Make a GraphQL call to the DB to get more details about the project associated with the given
project_id
. More details includea. name of the project b. plan - paid or free c. tenant - id, region, cloud, fqdn d. owner - id, email
-
Validation: Check if the fetched project is a cloud one, if it's a self hosted project throw an error.
-
Make a network call to LaunchDarkly to evaluate if the feature is enabled for the current project. LaunchDarkly returns a Boolean to indicate if the project can access the feature.
-
If the feature is not enabled for the project, throw an error saying the same.
-
If the feature is enabled, create a new record in the
jobs
table and return theid
of the inserted row. Thisid
is returned as the output of the action request.
Input: project_id
Output:
type BillingManagerInvitation struct {
ID *uuid.UUID `json:"id,omitempty"`
ProjectID *uuid.UUID `json:"project_id,omitempty"`
ManagerEmail string `json:"manager_email,omitempty"`
InvitedBy *uuid.UUID `json:"invited_by,omitempty"`
InvitedAt string `json:"invited_at,omitempty"`
Key string `json:"key,omitempty"`
AcceptedAt *string `json:"accepted_at,omitempty"`
DeclinedAt *string `json:"declined_at,omitempty"`
ResendInvitationCount int `json:"resend_invitation_count,omitempty"`
// Object RelationShip
InvitedByUser *User `json:"invited_by_user,omitempty"`
Project *Project `json:"project,omitempty"`
}
- Update the
project_billing_manager_invitations
with the followingset
andwhere
:
set:
declined_at: now()
where:
project_id:
_eq: project_id
accepted_at:
_is_null: true
declined_at:
_is_null: true
_or:
- project:
owner_id:
_eq: user-id
- project:
billing_manager_id:
_eq: user_id
- manager_email:
_eq: email
- project:
-- Complex boolean expression which checks if the given user id is an active collaborator or not
Idea: Response transform for regular mutations
-
If the
affected_rows
of the above update is 0, then throw a custom error message. -
Otherwise return the
id
,project_id
,manager_email
,invited_by
andinvited_at
.
Note: I'm not entirely sure why this is an action because it seems that this can be done using an update mutation itself. The only thing I can think of to model this as an action is to throw a custom error message.
Input: github_integration_config_id
Output:
{
"status": "success"
}
- Make the following graphql query:
query QueryGithubIntegration {
github_integration_config(where: {id: {_eq: $github_integration_config_id}}) {
project_id
organisation
repository
branch
directory
mode
email_status
}
}
Throw error if the github integration config is not found.
- Using the data from the previous response make another request to see if there have been any new commits since the last deployment. This is done by making the following GraphQL request:
query QueryGithubPushEvent {
github_push_event(where: $where, limit: 1, order_by: {commit_timestamp: desc}) {
commit
archive_url
id
}
}
variables:
{
"where": {
"push_metadata": {
"_contains_": {
"organisation": <configDetails.organisation>,
"repository": <configDetails.repository>,
"branch": <configDetails.branch>,
"directory": <configDetails.directory>
}
},
"github_integration_config_id": {
"_eq": github_integration_config_id
}
}
}
Validation: If no rows are returned by the above query then throw error.
-
Complex validation:
Check if the user that requested to deploy the latest commit is either the owner of the project or is an collaborator with admin priviliges and only then proceed with the actual deployment of the latest commit.
The above check is done by making a query to the
users
table. -
Create a job that contains the
archive_url
,branch
,directory
,project_id
,email
and insert it into thejobs
table.
input: project_id
, cloud
, region
, tenantGroupId
output:
{
"project_id": project_id
}
-
Get the role of the user who made the request.
-
[static input validation] Either the
tenantGroupID
should be set or the(cloud, region)
should be set. -
If the role is a non admin one, Check if the user who requested to move the project has access to move the project to another region. A bunch of validations are done here:
- The
plan_name
should becloud_payg
i.e. paid project. - The user should either be the owner of the project or an active collaborator of the project with admin privileges.
- Check if the project's tenant is not in Maintenance mode.
- Check if the project's tenant is active.
- The
-
Business logic: Move the project to the requested region and get the tenant's FQDN where the project was moved.
input: invoice_id
output: invoice_url
, receipt_url
- Make a GraphQL request to fetch the invoice (
invoice_url
,receipt_url
,updated_at
) from theinvoice
table wherestripe_invoice_id = $.input.invoice_id
. - Throw error if no invoice found or the invoice doesn't contain an
updated_at
timestamp. - Check if the invoice is older than 30 days by comparing the current timestamp to the invoice's
updated_at
field. - If the difference calculated in step iii is greater than 30 days, then make an external call to strip using the invoice_id to fetch the new invoice.
- If the returned invoice contains a
charge
then make another external call to Stripe to fetch the corresponding receipt URL using the charge'sid
. - Return the fetched
invoice_url
and thereceipt_url
(if available).
input: invoice_id
, payment_method_id
output: status
- Validation: Check if
x-hasura-user-email
andx-hasura-user-id
are notnull
. - Fetch the
id
of the payment method and the invoice corresponding to theinvoice_id
with a GraphQL request. - Validation: Check if the invoice's collection method is automatic, otherwise throw error.
- Make an external call to stripe to pay the invoice.
Input: project_id
Output: isAuthenticated
, email
, databaseUrl
, envVar
, integrationId
-
Validation: If the
project_id
is provided, check if the user who requested to create the neon database has admin priviliges or is the owner for the project corresponding to theproject_id
. If theproject_id
isnull
, then the above validation is not done. -
Create a neon HTTP client which will be used to interact with neon.
Steps to create the neon HTTP client:
-
[EXTERNAL CALL] To interact with Neon, first the client needs to get authenticated with Neon and after a successful authentication, a
Session
is created and stored in Hasura's vault corresponding to thex-hasura-user-id
of the user who made the request.A session is defined as follows:
type Session struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` ExpiresAt int `json:"expires_at"` TokenType string `json:"token_type"` }
- [EXTERNAL CALL] Fetch the existing session from the Hasura vault corresponding to the `x-hasura-user-id`. - [EXTERNAL CALL] If the session has expired or the user's authenticity couldn't be verified with Neon then refresh the Neon session to get a new session. If error in refreshing the session, then - [EXTERNAL CALL] delete the stored session from the vault. else - [EXTERNAL CALL] update the stored session with the new session obtained in the vault.
-
-
[External call] Get the user info associated with the current neon session. If the external call fails it is assumed that the user has not authenticated yet and the frontend would prompt a login automatically.
-
Business logic: Make a POST request to Neon to create a project and passing the access token present in the session in the
Authorization
header. -
Read the status code and handle the >500, 401 and 400 status differently and then combine the status and the message in the body to form an informative error message.
When the response status is 200, then metadata about the project created on Neon is expected in the response. A project is defined as below:
type Project struct { Name string `json:"name"` ID string `json:"id"` Roles []ProjectRole `json:"roles"` Databases []ProjectDatabase `json:"databases"` } type ProjectRole struct { Dsn string `json:"dsn"` Name string `json:"name"` } type ProjectDatabase struct { Id int64 `json:"id"` Name string `json:"name"` }
Validation: Check that the
id
or thename
of the project created is not empty. -
Get the DB URL from the response returned. The DB URL is present in the
ProjectRole
type in theDsn
key and consider only aProjectRole
whereName != "web-access"
. Throw error if no DB URL was found. -
The DB URL is modified to add options like
ssl=require
and include the neon project id in theoptions
key of the URL. -
A
neon_db_integration
table exists which contains the mappings of Hasura'sproject_id
to neon'sproject_id
. A new row is now inserted in the said table. -
Send the response to the client:
{ "isAuthenticated": true, "email": userInfo.email, // From step iii "databaseUrl": dbUrl // from step vii }