Skip to content

Remove integration-specific options from contracts#3704

Merged
jkachel merged 6 commits into
mainfrom
jkachel/drop-sso-contract-opts
Jun 29, 2026
Merged

Remove integration-specific options from contracts#3704
jkachel merged 6 commits into
mainfrom
jkachel/drop-sso-contract-opts

Conversation

@jkachel

@jkachel jkachel commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

What are the relevant tickets?

Closes https://github.com/mitodl/hq/issues/11657

Description (What does it do?)

This PR:

  • Migrates all contracts with membership_type set to sso or non-sso to managed and code, respectively.
  • Drops the integration_type field from ContractPage.
  • Drops the sso and non-sso type choices.
  • Updates places where the code references these now-removed things.

How can this be tested?

Automated tests should pass, and the system should work in general. The B2B system specifically should work as expected - you should be able to create contracts, add courseware, etc. and everything that is expected to happen should happen.

Additionally, the auto-attach functionality should be tested:

  • Create an org in Keycloak, and a corresponding org in MITx Online. (The sync_keycloak_orgs command may be helpful here.)
  • Create a contract for the org. Ensure it is set up properly for auto-attach: type managed, no price, no seat limit.
  • Create a second contract for the org. Ensure that it is not set up for auto-attach - use membership type code.
  • Sign in with a new user. That is, go through the registration process fresh; don't use an existing Keycloak account, etc.
  • Ensure the account looks correct in MITx Online - it should have no B2B contracts or orgs.
  • Log out of MITx Online. (Ideally, clear cookies or just close out your incognito session to make sure you don't have any stale data hanging out.)
  • In Keycloak, add the user to the org you created earlier.
  • Log back into MITx Online using the new user.
  • Check the user's account. You should see that they're now in the org, and that they're in the first contract, but not the second.
  • Log out of MITx Online again (and clear cookies, reset incognito, etc.)
  • In Keycloak, remove the user from the org.
  • Log back into MITx Online for a third time with the new user.
  • Check the user's account. They should now no longer be in the auto-attach contract or in the org.

If you wish you can also add them to the contract with an enrollment code, and then test adding and removing the user from the Keycloak org again; they should remain in the contract. (This functionality didn't change, so it should work as expected - if there's questions about what the behavior should be, double-check against main.)

The migration in this PR is reversible. I didn't add any new options so it does not set anything back to use sso or non-sso, but it does make sure the integration_type field is re-populated from membership_type.

Additional Context

As noted elsewhere, the contract doesn't specify whether or not users are to log in via SSO or not. It does specify whether or not the user gets access to the contract based solely on their membership in the org, though.

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown

OpenAPI Changes

Show/hide ## Changes for v0.yaml:
## Changes for v0.yaml:
24 changes: 23 error, 0 warning, 1 info
error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API POST /api/v0/b2b/attach/{enrollment_code}/
		removed the required property `items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/b2b/contracts/
		removed the required property `items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/b2b/contracts/{contract_slug}/
		removed the required property `integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/b2b/manager/organizations/
		removed the required property `items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/b2b/manager/organizations/{id}/
		removed the required property `contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/b2b/manager/organizations/{parent_lookup_organization}/contracts/{id}/
		removed the required property `integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/b2b/organizations/
		removed the required property `items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/b2b/organizations/{organization_slug}/
		removed the required property `contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/discounts/{parent_lookup_discount}/assignees/
		removed the required property `results/items/user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API POST /api/v0/discounts/{parent_lookup_discount}/assignees/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `201` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/discounts/{parent_lookup_discount}/assignees/{id}/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API PATCH /api/v0/discounts/{parent_lookup_discount}/assignees/{id}/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API PUT /api/v0/discounts/{parent_lookup_discount}/assignees/{id}/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/
		removed the required property `results/items/redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API POST /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `201` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/{id}/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API PATCH /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/{id}/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API PUT /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/{id}/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/userinfo/
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/users/current_user/
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API PATCH /api/v0/users/current_user/
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API GET /api/v0/users/me
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v0.yaml
	in API PATCH /api/v0/users/me
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

info	[api-schema-removed]
	in components/schemas
		removed the schema `IntegrationTypeEnum`



## Changes for v1.yaml:
24 changes: 23 error, 0 warning, 1 info
error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API POST /api/v0/b2b/attach/{enrollment_code}/
		removed the required property `items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/b2b/contracts/
		removed the required property `items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/b2b/contracts/{contract_slug}/
		removed the required property `integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/b2b/manager/organizations/
		removed the required property `items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/b2b/manager/organizations/{id}/
		removed the required property `contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/b2b/manager/organizations/{parent_lookup_organization}/contracts/{id}/
		removed the required property `integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/b2b/organizations/
		removed the required property `items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/b2b/organizations/{organization_slug}/
		removed the required property `contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/discounts/{parent_lookup_discount}/assignees/
		removed the required property `results/items/user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API POST /api/v0/discounts/{parent_lookup_discount}/assignees/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `201` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/discounts/{parent_lookup_discount}/assignees/{id}/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API PATCH /api/v0/discounts/{parent_lookup_discount}/assignees/{id}/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API PUT /api/v0/discounts/{parent_lookup_discount}/assignees/{id}/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/
		removed the required property `results/items/redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API POST /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `201` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/{id}/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API PATCH /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/{id}/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API PUT /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/{id}/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/userinfo/
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/users/current_user/
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API PATCH /api/v0/users/current_user/
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API GET /api/v0/users/me
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v1.yaml
	in API PATCH /api/v0/users/me
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

info	[api-schema-removed]
	in components/schemas
		removed the schema `IntegrationTypeEnum`



## Changes for v2.yaml:
24 changes: 23 error, 0 warning, 1 info
error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API POST /api/v0/b2b/attach/{enrollment_code}/
		removed the required property `items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/b2b/contracts/
		removed the required property `items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/b2b/contracts/{contract_slug}/
		removed the required property `integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/b2b/manager/organizations/
		removed the required property `items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/b2b/manager/organizations/{id}/
		removed the required property `contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/b2b/manager/organizations/{parent_lookup_organization}/contracts/{id}/
		removed the required property `integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/b2b/organizations/
		removed the required property `items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/b2b/organizations/{organization_slug}/
		removed the required property `contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/discounts/{parent_lookup_discount}/assignees/
		removed the required property `results/items/user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API POST /api/v0/discounts/{parent_lookup_discount}/assignees/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `201` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/discounts/{parent_lookup_discount}/assignees/{id}/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API PATCH /api/v0/discounts/{parent_lookup_discount}/assignees/{id}/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API PUT /api/v0/discounts/{parent_lookup_discount}/assignees/{id}/
		removed the required property `user/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/
		removed the required property `results/items/redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API POST /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `201` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/{id}/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API PATCH /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/{id}/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API PUT /api/v0/discounts/{parent_lookup_redeemed_discount}/redemptions/{id}/
		removed the required property `redeemed_by/b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/userinfo/
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/users/current_user/
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API PATCH /api/v0/users/current_user/
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API GET /api/v0/users/me
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

error	[response-required-property-removed] at head/openapi/specs/v2.yaml
	in API PATCH /api/v0/users/me
		removed the required property `b2b_organizations/items/contracts/items/integration_type` from the response with the `200` status

info	[api-schema-removed]
	in components/schemas
		removed the schema `IntegrationTypeEnum`



Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

@dsubak dsubak self-requested a review June 26, 2026 13:18
Comment thread b2b/api_test.py Outdated
contract = factories.ContractPageFactory(
integration_type=CONTRACT_MEMBERSHIP_SSO
membership_type=CONTRACT_MEMBERSHIP_MANAGED
if is_sso

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an ultra-nit, but we could switch the arg names referencing sso at this point to use the term managed instead for consistency if we wanted to (is_sso -> is_managed and update_sso -> update_managed)

@dsubak dsubak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still owe you some manual testing before I can approve, but it looks pretty straightforward mechanically. Big fan of making the contract models a bit less crufty!

Comment thread b2b/management/commands/b2b_contract.py Outdated
"membership_type",
type=str,
help="The membership type for this contract.",
choices=[value[0] for value in CONTRACT_MEMBERSHIP_CHOICES],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think this constant got dropped - we'll need to fix the import and this choice valueset

@jkachel jkachel force-pushed the jkachel/drop-sso-contract-opts branch from e784563 to 54fe51d Compare June 26, 2026 20:04

@dsubak dsubak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is looking good! After a bit of hullabaloo fighting keycloak, I was able to step through the manual testing and get my test user added to the contract on login while in the KC Org and removed from it while removed.

@jkachel jkachel force-pushed the jkachel/drop-sso-contract-opts branch from 54fe51d to 55ba8c4 Compare June 29, 2026 18:06
@jkachel jkachel merged commit 79f46ec into main Jun 29, 2026
9 checks passed
@jkachel jkachel deleted the jkachel/drop-sso-contract-opts branch June 29, 2026 18:27
This was referenced Jun 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants