Skip to content
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

landing_job: add landed_revisions field (bug 1834885) #318

Merged
merged 5 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions landoapi/api/transplants.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ def post(phab: PhabricatorClient, data: dict):

# Submit landing job.
job.status = LandingJobStatus.SUBMITTED
job.set_landed_revision_diffs()
db.session.commit()

logger.info(f"New landing job {job.id} created for {landing_repo.tree} repo.")
Expand Down
31 changes: 28 additions & 3 deletions landoapi/models/landing_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,20 @@ class LandingJob(Base):
)

@property
def landing_path(self) -> list[tuple[int, int]]:
return [(revision.revision_id, revision.diff_id) for revision in self.revisions]
def landed_revisions(self):
zzzeid marked this conversation as resolved.
Show resolved Hide resolved
"""Return revision and diff ID mapping associated with the landing job."""
revision_ids = [revision.id for revision in self.revisions]
revision_to_diff_ids_query = (
revision_landing_job.select()
.join(Revision)
.where(
revision_landing_job.c.revision_id.in_(revision_ids),
revision_landing_job.c.landing_job_id == self.id,
)
.with_only_columns(Revision.revision_id, revision_landing_job.c.diff_id)
.order_by(revision_landing_job.c.index)
)
return dict(list(db.session.execute(revision_to_diff_ids_query)))

@property
def serialized_landing_path(self):
Expand All @@ -147,7 +159,7 @@ def serialized_landing_path(self):
"revision_id": "D{}".format(revision_id),
"diff_id": diff_id,
}
for revision_id, diff_id in self.landing_path
for revision_id, diff_id in self.landed_revisions.items()
]
else:
return [
Expand Down Expand Up @@ -249,6 +261,19 @@ def sort_revisions(self, revisions: list[Revision]):
.values(index=index)
)

def set_landed_revision_diffs(self):
"""Assign diff_ids, if available, to each association row."""
# Update association table records with current diff_id values.
for revision in self.revisions:
db.session.execute(
revision_landing_job.update()
.where(revision_landing_job.c.landing_job_id == self.id)
.where(
revision_landing_job.c.revision_id == revision.id,
)
zzzeid marked this conversation as resolved.
Show resolved Hide resolved
.values(diff_id=revision.diff_id)
)

def transition_status(
self,
action: LandingJobAction,
Expand Down
2 changes: 2 additions & 0 deletions landoapi/models/revisions.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ class DiffWarningGroup(enum.Enum):


# Association table with custom "index" column to guarantee sorting of revisions.
# The diff_id column is used as a transaction record of the diff_id at landing time.
revision_landing_job = db.Table(
"revision_landing_job",
db.Column("landing_job_id", db.ForeignKey("landing_job.id")),
db.Column("revision_id", db.ForeignKey("revision.id")),
db.Column("index", db.Integer),
db.Column("diff_id", db.Integer, nullable=True),
)


Expand Down
8 changes: 3 additions & 5 deletions landoapi/transplants.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,11 @@ def warning_previously_landed(*, revision, diff, **kwargs):
if job is None:
return None

revision_to_diff_id = {
revision.revision_id: revision.diff_id for revision in job.revisions
}
revision_to_diff_id = job.landed_revisions
if job.revision_to_diff_id:
legacy_data = {
int(revision_id): int(diff_id)
for revision_id, diff_id in job.revision_to_diff_id.items()
int(_revision_id): int(_diff_id)
for _revision_id, _diff_id in job.revision_to_diff_id.items()
}
zzzeid marked this conversation as resolved.
Show resolved Hide resolved
revision_to_diff_id.update(legacy_data)
landed_diff_id = revision_to_diff_id[revision_id]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"""add revision_landing_job.diff_id

Revision ID: 50ffadceca83
Revises: a8cd4d43c4c3
Create Date: 2023-05-25 01:50:36.755884

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = "50ffadceca83"
down_revision = "a8cd4d43c4c3"
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"revision_landing_job", sa.Column("diff_id", sa.Integer(), nullable=True)
)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("revision_landing_job", "diff_id")
# ### end Alembic commands ###
82 changes: 82 additions & 0 deletions tests/test_transplants.py
Original file line number Diff line number Diff line change
Expand Up @@ -711,6 +711,88 @@ def test_integrated_transplant_simple_stack_saves_data_in_db(
(r3["id"], d3["id"]),
]
assert job.status == LandingJobStatus.SUBMITTED
assert job.landed_revisions == {1: 1, 2: 2, 3: 3}


def test_integrated_transplant_updated_diff_id_reflected_in_landed_revisions(
db,
client,
phabdouble,
auth0_mock,
release_management_project,
register_codefreeze_uri,
):
# Performs a similar simple test as in
# test_integrated_transplant_simple_stack_saves_data_in_db but submit an additional
# landing job for an updated revision diff.
zzzeid marked this conversation as resolved.
Show resolved Hide resolved
repo = phabdouble.repo()
user = phabdouble.user(username="reviewer")

d1a = phabdouble.diff()
r1 = phabdouble.revision(diff=d1a, repo=repo)
phabdouble.reviewer(r1, user)

response = client.post(
"/transplants",
json={
"landing_path": [
{"revision_id": "D{}".format(r1["id"]), "diff_id": d1a["id"]},
]
},
headers=auth0_mock.mock_headers,
)
assert response.status_code == 202
assert response.content_type == "application/json"
assert "id" in response.json
job_id = response.json["id"]

# Ensure DB access isn't using uncommitted data.
db.session.close()

# Get LandingJob object by its id
zzzeid marked this conversation as resolved.
Show resolved Hide resolved
job = LandingJob.query.get(job_id)
assert job.id == job_id
assert [(revision.revision_id, revision.diff_id) for revision in job.revisions] == [
(r1["id"], d1a["id"]),
]
assert job.status == LandingJobStatus.SUBMITTED
assert job.landed_revisions == {r1["id"]: d1a["id"]}

# Cancel job.
response = client.put(
f"/landing_jobs/{job.id}",
json={"status": "CANCELLED"},
headers=auth0_mock.mock_headers,
)

job = LandingJob.query.get(job_id)
assert job.status == LandingJobStatus.CANCELLED

d1b = phabdouble.diff(revision=r1)
phabdouble.reviewer(r1, user)
response = client.post(
"/transplants",
json={
"landing_path": [
{"revision_id": "D{}".format(r1["id"]), "diff_id": d1b["id"]},
]
},
headers=auth0_mock.mock_headers,
)

job_id = response.json["id"]

# Ensure DB access isn't using uncommitted data.
db.session.close()

# Get LandingJob object by its id
zzzeid marked this conversation as resolved.
Show resolved Hide resolved
job = LandingJob.query.get(job_id)
assert job.id == job_id
assert [(revision.revision_id, revision.diff_id) for revision in job.revisions] == [
(r1["id"], d1b["id"]),
]
assert job.status == LandingJobStatus.SUBMITTED
assert job.landed_revisions == {r1["id"]: d1b["id"]}
zzzeid marked this conversation as resolved.
Show resolved Hide resolved


def test_integrated_transplant_with_flags(
Expand Down