- 
                Notifications
    You must be signed in to change notification settings 
- Fork 196
Arc 1192 update backfilling status #2482
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
          
     Open
      
      
            kamaksheeAtl
  wants to merge
  63
  commits into
  main
  
    
      
        
          
  
    
      Choose a base branch
      
     
    
      
        
      
      
        
          
          
        
        
          
            
              
              
              
  
           
        
        
          
            
              
              
           
        
       
     
  
        
          
            
          
            
          
        
       
    
      
from
ARC-1192-update-backfilling-status
  
      
      
   
  
    
  
  
  
 
  
      
    base: main
Could not load branches
            
              
  
    Branch not found: {{ refName }}
  
            
                
      Loading
              
            Could not load tags
            
            
              Nothing to show
            
              
  
            
                
      Loading
              
            Are you sure you want to change the base?
            Some commits from the old base branch may be removed from the timeline,
            and old review comments may become outdated.
          
          
  
     Open
                    Changes from 60 commits
      Commits
    
    
            Show all changes
          
          
            63 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      28ee606
              
                chore: spike for backfill status fetch
              
              
                kamaksheeAtl bb37377
              
                chore: spike for backfill status fetch
              
              
                kamaksheeAtl 44a779b
              
                chore: spike for backfill status fetch
              
              
                kamaksheeAtl c8b0cf1
              
                chore: add progress fetch fr backfill pg
              
              
                kamaksheeAtl 93cb05e
              
                chore: update test snapshots
              
              
                kamaksheeAtl e7cb1c7
              
                chore: remove get-connected-repo changes
              
              
                kamaksheeAtl 291ac6d
              
                chore: remove get-connected-repo changes
              
              
                kamaksheeAtl 973ee29
              
                chore: update test snapshots
              
              
                kamaksheeAtl 99aec7a
              
                fix: missing subscription id in data attri
              
              
                kamaksheeAtl 603dc2a
              
                chore: update failing test cases
              
              
                kamaksheeAtl 3d7ddb7
              
                chore: incorporate PR review comments
              
              
                kamaksheeAtl e54d420
              
                chore: incorporate PR review comments
              
              
                kamaksheeAtl 5094704
              
                chore: incorporate pr review comments
              
              
                kamaksheeAtl 463fb6e
              
                chore: fix test cases and add security middleware to new api route
              
              
                kamaksheeAtl b9bcf6b
              
                chore: update test cases to fix failing test
              
              
                kamaksheeAtl f123a7f
              
                chore: update api for 403 incase of missmached jirahost
              
              
                kamaksheeAtl a5404df
              
                fix: failing test cases
              
              
                kamaksheeAtl 07edd59
              
                chore: add the backfill since status
              
              
                kamaksheeAtl f3701e9
              
                chore: update code as per PR review comment
              
              
                kamaksheeAtl dd6c60a
              
                chore: update the jwt in backfill call
              
              
                kamaksheeAtl 1d61632
              
                chore: update the jwt in backfill call
              
              
                kamaksheeAtl bc4286b
              
                chore: merge main
              
              
                kamaksheeAtl b267700
              
                chore: add test cases
              
              
                kamaksheeAtl 10aae00
              
                chore: add test cases
              
              
                kamaksheeAtl 195d2b6
              
                chore: add test cases
              
              
                kamaksheeAtl c7312a4
              
                chore: update test cases to increase the coverage
              
              
                kamaksheeAtl 2c4a3b1
              
                chore: remove explicit jwt pass
              
              
                kamaksheeAtl 7887a8f
              
                chore: PR review comments
              
              
                kamaksheeAtl 637389d
              
                chore: PR review comments
              
              
                kamaksheeAtl 63c421e
              
                chore: PR review comments
              
              
                kamaksheeAtl 50c78f2
              
                chore: update testcases
              
              
                kamaksheeAtl a023315
              
                chore: update snaps
              
              
                kamaksheeAtl 4df488e
              
                chore: add feature flag
              
              
                kamaksheeAtl 09b5c30
              
                chore: update space changes
              
              
                kamaksheeAtl 9a5ed36
              
                chore: update space changes
              
              
                kamaksheeAtl bec3e9c
              
                Merge branch 'main' into ARC-1192-update-backfilling-status
              
              
                kamaksheeAtl d95c309
              
                fix: zero repo count scenario
              
              
                kamaksheeAtl 87dac3c
              
                Merge branch 'main' into ARC-1192-update-backfilling-status
              
              
                kamaksheeAtl d98c7d5
              
                fix: give results of subs with totalrepos more than zero
              
              
                kamaksheeAtl a47cff2
              
                Merge branch 'main' into ARC-1192-update-backfilling-status
              
              
                kamaksheeAtl 8545cab
              
                chore: simplify the logic
              
              
                kamaksheeAtl 40fc18c
              
                chore: chng logic of detected backfill complete
              
              
                kamaksheeAtl 61e6241
              
                fix: org with zero repo count display
              
              
                kamaksheeAtl 2d874a8
              
                Merge branch 'main' into ARC-316-fix-zerorepo-org
              
              
                kamaksheeAtl 7138c4b
              
                fix: org with zero repo count display
              
              
                kamaksheeAtl dca1b90
              
                fix: org with zero repo count display
              
              
                kamaksheeAtl d332cec
              
                chore: merge main brnch
              
              
                kamaksheeAtl 20ea125
              
                chore: merge main branch
              
              
                kamaksheeAtl f8615bb
              
                chore: add sync err handling
              
              
                kamaksheeAtl a99a7ff
              
                chore: modify test cases
              
              
                kamaksheeAtl 208e8f9
              
                chore: modify test cases
              
              
                kamaksheeAtl 279e950
              
                chore: merge main
              
              
                kamaksheeAtl 737f208
              
                chore: reformate code
              
              
                kamaksheeAtl cd2ba30
              
                chore: reformate code
              
              
                kamaksheeAtl 6d04509
              
                chore: reformate code
              
              
                kamaksheeAtl d56a78e
              
                chore: reformate code
              
              
                kamaksheeAtl 6b308f6
              
                chore: PR comment refactor
              
              
                kamaksheeAtl 7929f06
              
                Merge branch 'main' into ARC-1192-update-backfilling-status
              
              
                kamaksheeAtl 9cfa423
              
                Merge branch 'main' into ARC-1192-update-backfilling-status
              
              
                kamaksheeAtl 7446db2
              
                chore: PR comments
              
              
                kamaksheeAtl b1d3aa3
              
                chore: PR comments
              
              
                kamaksheeAtl 13677c9
              
                chore: PR comments
              
              
                kamaksheeAtl 5e1d563
              
                Merge branch 'main' into ARC-1192-update-backfilling-status
              
              
                kamaksheeAtl File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
        
          
  
    
      
          
            191 changes: 191 additions & 0 deletions
          
          191 
        
  src/routes/jira/jira-get-connections-backfill-status.test.ts
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| import { getFrontendApp } from "~/src/app"; | ||
| import { Installation } from "models/installation"; | ||
| import { encodeSymmetric } from "atlassian-jwt"; | ||
| import { getLogger } from "config/logger"; | ||
| import { Subscription } from "models/subscription"; | ||
| import { DatabaseStateCreator } from "test/utils/database-state-creator"; | ||
| import supertest from "supertest"; | ||
| import { booleanFlag, BooleanFlags } from "config/feature-flags"; | ||
| import { when } from "jest-when"; | ||
| import { RepoSyncState } from "models/reposyncstate"; | ||
|  | ||
| jest.mock("config/feature-flags"); | ||
|  | ||
| describe("jira-get-connections-backfillStatus.test", () => { | ||
| let app; | ||
| let installation: Installation; | ||
| let subscription: Subscription; | ||
| let repoSyncState: RepoSyncState; | ||
| const generateJwt = async () => { | ||
| return encodeSymmetric( | ||
| { | ||
| qsh: "context-qsh", | ||
| iss: installation.plainClientKey, | ||
| sub: "myAccountId" | ||
| }, | ||
| await installation.decrypt("encryptedSharedSecret", getLogger("test")) | ||
| ); | ||
| }; | ||
|  | ||
| beforeEach(async () => { | ||
| app = getFrontendApp(); | ||
| const result = await new DatabaseStateCreator() | ||
| .withActiveRepoSyncState() | ||
| .create(); | ||
| installation = result.installation; | ||
| subscription = result.subscription; | ||
| repoSyncState = result.repoSyncState!; | ||
| when(booleanFlag) | ||
| .calledWith(BooleanFlags.JIRA_ADMIN_CHECK) | ||
| .mockResolvedValue(true); | ||
| }); | ||
|  | ||
| it("should return 401 when no JWT was provided", async () => { | ||
| const resp = await supertest(app).get( | ||
| `/jira/subscriptions/backfill-status/?subscriptionIds=${subscription.id}` | ||
| ); | ||
| expect(resp.status).toStrictEqual(401); | ||
| expect(resp.text).toBe("Unauthorised"); | ||
| }); | ||
|  | ||
| it("should return 403 when not an admin", async () => { | ||
| const resp = await supertest(app) | ||
| .get( | ||
| `/jira/subscriptions/backfill-status?subscriptionIds=${subscription.id}` | ||
| ) | ||
| .set( | ||
| "authorization", | ||
| `JWT ${await generateJwt()}` | ||
| ); | ||
| expect(resp.status).toStrictEqual(403); | ||
| }); | ||
|  | ||
| describe("admin and JWT are OK", () => { | ||
| beforeEach(() => { | ||
| const payload = { | ||
| accountId: "myAccountId", | ||
| globalPermissions: ["ADMINISTER"] | ||
| }; | ||
| jiraNock | ||
| .post("/rest/api/latest/permissions/check", payload) | ||
| .reply(200, { globalPermissions: ["ADMINISTER"] }); | ||
| }); | ||
|  | ||
| it("should return 400 when no subscriptions were found", async () => { | ||
| const resp = await supertest(app) | ||
| .get( | ||
| `/jira/subscriptions/backfill-status?subscriptionIds=${ | ||
| subscription.id + 1 | ||
| }` | ||
| ) | ||
| .set( | ||
| "authorization", | ||
| `JWT ${await generateJwt()}` | ||
| ); | ||
| expect(resp.status).toStrictEqual(400); | ||
| }); | ||
|  | ||
| it("should return 400 when no Missing Subscription IDs were found in query", async () => { | ||
| const resp = await supertest(app) | ||
| .get(`/jira/subscriptions/backfill-status`) | ||
| .set("authorization", `JWT ${await generateJwt()}`); | ||
| expect(resp.status).toStrictEqual(400); | ||
| expect(resp.text).toBe("Missing Subscription IDs"); | ||
| }); | ||
|  | ||
| it("should return 403 if the subscription belongs to a different user", async () => { | ||
| const result = await new DatabaseStateCreator() | ||
| .forJiraHost("https://another-one.atlassian.net") | ||
| .create(); | ||
| const resp = await supertest(app) | ||
| .get( | ||
| `/jira/subscriptions/backfill-status?subscriptionIds=${result.subscription.id}` | ||
| ) | ||
| .set( | ||
| "authorization", | ||
| `JWT ${await generateJwt()}` | ||
| ); | ||
|  | ||
| expect(resp.status).toStrictEqual(403); | ||
| }); | ||
|  | ||
| it("should return 200 if the subscription belongs to the same user", async () => { | ||
| const resp = await supertest(app) | ||
| .get( | ||
| `/jira/subscriptions/backfill-status?subscriptionIds=${subscription.id}` | ||
| ) | ||
| .set( | ||
| "authorization", | ||
| `JWT ${await generateJwt()}` | ||
| ); | ||
| expect(resp.status).toStrictEqual(200); | ||
| }); | ||
|  | ||
| describe("happy paths", () => { | ||
| beforeEach(async () => { | ||
| const newRepoSyncStatesData: any[] = []; | ||
| for (let newRepoStateNo = 1; newRepoStateNo < 50; newRepoStateNo++) { | ||
| const newRepoSyncState = { ...repoSyncState.dataValues }; | ||
| delete newRepoSyncState["id"]; | ||
| delete newRepoSyncState["commitStatus"]; | ||
| delete newRepoSyncState["branchStatus"]; | ||
| newRepoSyncState["repoId"] = repoSyncState.repoId + newRepoStateNo; | ||
| newRepoSyncState["repoName"] = | ||
| repoSyncState.repoName + newRepoStateNo.toString(); | ||
| newRepoSyncState["repoFullName"] = | ||
| repoSyncState.repoFullName + | ||
| String(newRepoStateNo).padStart(3, "0"); | ||
| if (newRepoStateNo === 1) { | ||
| newRepoSyncState["commitStatus"] = "pending"; | ||
| newRepoSyncState["branchStatus"] = "complete"; | ||
| newRepoSyncState["pullStatus"] = "complete"; | ||
| newRepoSyncState["buildStatus"] = "complete"; | ||
| newRepoSyncState["deploymentStatus"] = "pending"; | ||
| } else if (newRepoStateNo % 3 == 1) { | ||
| newRepoSyncState["commitStatus"] = "complete"; | ||
| newRepoSyncState["branchStatus"] = "complete"; | ||
| newRepoSyncState["pullStatus"] = "complete"; | ||
| newRepoSyncState["buildStatus"] = "complete"; | ||
| newRepoSyncState["deploymentStatus"] = "complete"; | ||
| } else if (newRepoStateNo % 3 == 2) { | ||
| newRepoSyncState["commitStatus"] = "failed"; | ||
| newRepoSyncState["branchStatus"] = "complete"; | ||
| newRepoSyncState["pullStatus"] = "complete"; | ||
| newRepoSyncState["buildStatus"] = "complete"; | ||
| newRepoSyncState["deploymentStatus"] = "failed"; | ||
| } | ||
| newRepoSyncStatesData.push(newRepoSyncState); | ||
| } | ||
| await RepoSyncState.bulkCreate(newRepoSyncStatesData); | ||
| }); | ||
|  | ||
| it("should return 200 if the subscription belongs to the same user", async () => { | ||
| const resp = await supertest(app) | ||
| .get( | ||
| `/jira/subscriptions/backfill-status?subscriptionIds=${subscription.id}` | ||
| ) | ||
| .set( | ||
| "authorization", | ||
| `JWT ${await generateJwt()}` | ||
| ); | ||
| expect(resp.status).toStrictEqual(200); | ||
|  | ||
| expect(resp.body).toMatchObject({ | ||
| data: { | ||
| subscriptions: { | ||
| [subscription.id]: { | ||
| isSyncComplete: false, | ||
| syncStatus: "IN PROGRESS", | ||
| totalRepos: 33, | ||
| syncedRepos: 17, | ||
| backfillSince: null | ||
| } | ||
| }, | ||
| isBackfillComplete: false, | ||
| subscriptionIds: [subscription.id] | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | 
        
          
  
    
      
          
            110 changes: 110 additions & 0 deletions
          
          110 
        
  src/routes/jira/jira-get-connections-backfill-status.ts
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| import { NextFunction, Request, Response } from "express"; | ||
| import { groupBy } from "lodash"; | ||
| import { RepoSyncState } from "~/src/models/reposyncstate"; | ||
| import { Subscription, SyncStatus } from "~/src/models/subscription"; | ||
| import { | ||
| mapSyncStatus, | ||
| ConnectionSyncStatus, | ||
| getRetryableFailedSyncErrors | ||
| } from "~/src/util/github-installations-helper"; | ||
|  | ||
| type SubscriptionBackfillState = { | ||
| totalRepos?: number; | ||
| syncedRepos?: number; | ||
| syncStatus: ConnectionSyncStatus; | ||
| isSyncComplete: boolean; | ||
| backfillSince?: string; | ||
| failedSyncErrors?: Record<string, number>; | ||
| syncWarning?: string; | ||
| }; | ||
|  | ||
| type BackFillType = { | ||
| [key: string]: SubscriptionBackfillState; | ||
| }; | ||
|  | ||
| export const JiraGetConnectionsBackfillStatus = async ( | ||
| req: Request, | ||
| res: Response, | ||
| next: NextFunction | ||
| ): Promise<void> => { | ||
| try { | ||
| const { jiraHost: localJiraHost } = res.locals; | ||
| const subscriptionIds = String(req.query?.subscriptionIds) | ||
| .split(",") | ||
| .map(Number) | ||
| .filter(Boolean); | ||
|  | ||
| if (subscriptionIds.length === 0) { | ||
| req.log.warn("Missing Subscription IDs"); | ||
| res.status(400).send("Missing Subscription IDs"); | ||
| return; | ||
| } | ||
|  | ||
| const subscriptions = await Subscription.findAllForSubscriptionIds(subscriptionIds); | ||
|  | ||
| const resultSubscriptionIds = subscriptions.map( | ||
| (subscription) => subscription.id | ||
| ); | ||
|  | ||
| if (!subscriptions || subscriptions.length === 0) { | ||
| req.log.error("Missing Subscription"); | ||
| res.status(400).send("Missing Subscription"); | ||
| return; | ||
| } | ||
|  | ||
| const jiraHosts = subscriptions.map( | ||
| (subscription) => subscription?.jiraHost | ||
| ); | ||
|  | ||
| const jiraHostsMatched = jiraHosts.every( | ||
|         
                  kamaksheeAtl marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| (jiraHost) => jiraHost === localJiraHost | ||
| ); | ||
|  | ||
| if (!jiraHostsMatched) { | ||
| req.log.error("mismatched Jira Host"); | ||
| res.status(403).send("mismatched Jira Host"); | ||
| return; | ||
| } | ||
| const subscriptionsById = groupBy(subscriptions, "id"); | ||
| const backfillStatus = await getBackfillStatus(subscriptionsById); | ||
| const isBackfillComplete = getBackfillCompletionStatus(backfillStatus); | ||
| res.status(200).send({ | ||
| data: { | ||
| subscriptions: backfillStatus, | ||
| isBackfillComplete, | ||
| subscriptionIds: resultSubscriptionIds | ||
| } | ||
| }); | ||
| } catch (error) { | ||
| return next(new Error(`Failed to render connected repos`)); | ||
|         
                  kamaksheeAtl marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| } | ||
| }; | ||
|  | ||
| const getBackfillCompletionStatus = (backfillStatus: BackFillType): boolean => | ||
| Object.values(backfillStatus).every( | ||
| (backFill: SubscriptionBackfillState): boolean => backFill?.isSyncComplete | ||
| ); | ||
|  | ||
| const getBackfillStatus = async (subscriptionsById): Promise<BackFillType> => { | ||
|          | ||
| const backfillStatus: BackFillType = {}; | ||
| for (const subscriptionId in subscriptionsById) { | ||
| const subscription = subscriptionsById[subscriptionId][0]; | ||
| const isSyncComplete = | ||
| subscription?.syncStatus === SyncStatus.COMPLETE || | ||
| subscription?.syncStatus === SyncStatus.FAILED; | ||
| const failedSyncErrors = await getRetryableFailedSyncErrors(subscription); | ||
|  | ||
| backfillStatus[subscriptionId] = { | ||
| isSyncComplete, | ||
| syncStatus: mapSyncStatus(subscription?.syncStatus), | ||
| totalRepos: subscription?.totalNumberOfRepos, | ||
| syncedRepos: await RepoSyncState.countFullySyncedReposForSubscription( | ||
|         
                  kamaksheeAtl marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| subscription | ||
| ), | ||
| failedSyncErrors, | ||
| backfillSince: subscription?.backfillSince || null, | ||
| syncWarning: subscription.syncWarning | ||
| }; | ||
| } | ||
| return backfillStatus; | ||
| }; | ||
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
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.
We have an enum set up for this in error.ts. Just import
MISSING_SUBSCRIPTION