import Sequelize , { CountOptions , DestroyOptions , FindOptions , Op } from "sequelize" ; import Subscription , { Repositories , RepositoryData , RepoSyncStateObject , TaskStatus } from "./subscription" ; import _ from "lodash" ; export default class RepoSyncState extends Sequelize . Model { id : number ; subscriptionId : number ; repoId : number ; repoName : string ; repoOwner : string ; repoFullName : string ; repoUrl : string ; priority ? : number ; branchStatus ? : TaskStatus ; commitStatus ? : TaskStatus ; issueStatus ? : TaskStatus ; pullStatus ? : TaskStatus ; buildStatus ? : TaskStatus ; deploymentStatus ? : TaskStatus ; branchCursor ? : string ; commitCursor ? : string ; issueCursor ? : string ; pullCursor ? : string ; buildCursor ? : string ; deploymentCursor ? : string ; forked ? : boolean ; repoPushedAt ? : Date ; repoUpdatedAt ? : Date ; repoCreatedAt ? : Date ; syncUpdatedAt ? : Date ; syncCompletedAt ? : Date ; updatedAt : Date ; createdAt : Date ; get status ( ) : TaskStatus { const statuses = [ this . pullStatus , this . branchStatus , this . commitStatus ] ; if ( statuses . some ( s => s === "failed" ) ) { return "failed" ; } if ( statuses . every ( s => s === "complete" ) ) { return "complete" ; } return "pending" ; } static async countSyncedReposFromSubscription ( subscription ) { return RepoSyncState . countFromSubscription ( subscription , { where : { pullStatus : "complete" , branchStatus : "complete" , commitStatus : "complete" } } ) ; } static async countFailedReposFromSubscription ( subscription ) { return RepoSyncState . countFromSubscription ( subscription , { where : { [ Op . or ] : { pullStatus : "failed" , branchStatus : "failed" , commitStatus : "failed" } } } ) ; } static async countFromSubscription ( subscription , options = { } ) { return RepoSyncState . count ( _ . merge ( options , { where : { subscriptionId : subscription . id } } ) ) ; } static async findAllFromSubscription ( subscription , options = { } ) { return RepoSyncState . findAll ( _ . merge ( options , { where : { subscriptionId : subscription . id } } ) ) ; } static async findOneFromSubscription ( subscription , options = { } ) { return RepoSyncState . findOne ( _ . merge ( options , { where : { subscriptionId : subscription . id } , order : [ [ "repoUpdatedAt" , "DESC" ] ] } as FindOptions ) ) ; } static async deleteFromSubscription ( subscription , options = { } ) { return RepoSyncState . destroy ( _ . merge ( options , { where : { subscriptionId : subscription . id } } ) ) ; } static async resetSyncFromSubscription ( subscription ) { return RepoSyncState . update ( { repoUpdatedAt : null , branchStatus : null , branchCursor : null , commitStatus : null , commitCursor : null , pullStatus : null , pullCursor : null } , { where : { subscriptionId : subscription . id } } ) ; } static async updateFromRepoJson ( subscription , json = { } ) { const repoIds = Object . keys ( json . repos || { } ) ; const states = await RepoSyncState . findAll ( { where : { subscriptionId : subscription . id , repoId : { [ Op . in ] : repoIds } } } ) ; return RepoSyncState . sequelize ?. transaction ( async ( transaction ) => { await RepoSyncState . destroy ( { where : { subscriptionId : subscription . id , repoId : { [ Op . notIn ] : repoIds } } , transaction } ) ; return Promise . all ( repoIds . map ( id => { const repo = json . repos ?. [ id ] ; const model = states . find ( s => s . repoId === Number ( id ) ) || RepoSyncState . buildFromRepositoryData ( subscription , repo ) ; return model ?. setFromRepositoryData ( repo ) . save ( { transaction } ) ; } ) ) ; } ) ; } static async toRepoJson ( subscription ) { const repos = await RepoSyncState . findAllFromSubscription ( subscription ) ; return { installationId : subscription . gitHubInstallationId , jiraHost : subscription . jiraHost , numberOfSyncedRepos : await RepoSyncState . countSyncedReposFromSubscription ( subscription ) , repos : repos . reduce < Repositories > ( ( acc , repo ) => { acc [ repo . repoId ] = repo . toRepositoryData ( ) ; return acc ; } , { } ) } ; } static buildFromRepositoryData ( subscription , repo ? ) { const repoId = Number ( repo ?. repository ?. id ) ; if ( ! repoId ) { return undefined ; } return RepoSyncState . build ( { repoId , subscriptionId : subscription . id , repoName : repo ?. repository ?. name , repoOwner : repo ?. repository ?. owner ?. login , repoFullName : repo ?. repository ?. full_name , repoUrl : repo ?. repository ?. html_url } ) ; } static async updateRepoForSubscription ( subscription , repoId , key , value ) { const model = await RepoSyncState . findOne ( { where : { subscriptionId : subscription . id , repoId } } ) ; const repo = _ . merge ( model ?. toRepositoryData ( ) || { } , { [ key ] : value } ) ; return model ?. setFromRepositoryData ( repo ) ?. save ( ) ; } setFromRepositoryData ( repo ? ) { if ( repo ) { this . repoUpdatedAt = new Date ( repo . repository ?. updated_at ?? Date . now ( ) ) ; this . branchStatus = repo . branchStatus ; this . branchCursor = repo . lastBranchCursor ; this . commitStatus = repo . commitStatus ; this . commitCursor = repo . lastCommitCursor ; this . pullStatus = repo . pullStatus ; this . pullCursor = repo . lastPullCursor ?. toString ( ) ; } return this ; } toRepositoryData ( ) { return _ . pickBy ( { repository : { id : this . repoId . toString ( ) , name : this . repoName , full_name : this . repoFullName , owner : { login : this . repoOwner } , html_url : this . repoUrl , updated_at : ( this . repoUpdatedAt ?? new Date ( 0 ) ) as any } , pullStatus : this . pullStatus , lastPullCursor : this . pullCursor ? Number ( this . pullCursor ) : undefined , commitStatus : this . commitStatus , lastCommitCursor : this . commitCursor , branchStatus : this . branchStatus , lastBranchCursor : this . branchCursor } ) ; } }