Skip to content

Commit

Permalink
feat(app-reg-v2): filter applications by auth_strategy_id (#385)
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewwylde authored Feb 6, 2024
1 parent 72159dc commit e991472
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 28 deletions.
65 changes: 55 additions & 10 deletions cypress/e2e/fixtures/consts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GetApplicationResponse, GetRegistrationResponse, PortalContext, Product, ProductVersion } from '@kong/sdk-portal-js'
import { GetApplicationResponse, GetRegistrationResponse, PortalContext, Product, ProductVersion, RegistrationConfiguration } from '@kong/sdk-portal-js'

const versions: ProductVersion[] = [
{
Expand All @@ -11,14 +11,23 @@ const versions: ProductVersion[] = [
}
]

const keyAuthRegConfig: RegistrationConfiguration = {
name: 'key auth auth strategy',
credential_type: 'key_auth',
id: 'd668b397-8a0d-482d-9b97-9e05cbfc7618'
}

const oidcAuthRegConfig: RegistrationConfiguration = {
auth_methods: ['bearer', 'client_credentials'],
name: 'oidc auth strategy',
credential_type: 'client_credentials',
id: '7b651144-0b48-431a-af9b-58604adc9268'
}

const versionWithOidcAuthStrategy: ProductVersion = {
...versions[0],
registration_configs: [
{
auth_methods: ['bearer', 'client_credentials'],
name: 'oidc auth strategy',
credential_type: 'client_credentials'
}
oidcAuthRegConfig
]
}

Expand All @@ -29,10 +38,7 @@ const versionWithKeyAuthAuthStrategy: ProductVersion = {
name: 'v1-beta',
deprecated: false,
registration_configs: [
{
name: 'key auth auth strategy',
credential_type: 'key_auth'
}
keyAuthRegConfig
]
}

Expand All @@ -50,6 +56,34 @@ const product: Product = {
version_count: 1
}

export const productWithKeyAuthAppAuthStrategy: Product = {
created_at: '2022-03-23T14:52:41.893Z',
updated_at: '2022-03-23T14:52:41.893Z',
id: '29985c03-a866-46f2-8152-29406243b90f',
name: 'barAPI',
description: null,
document_count: 0,
latest_version: {
id: versionWithKeyAuthAuthStrategy.id,
name: versionWithKeyAuthAuthStrategy.name
},
version_count: 1
}

export const productWithOidcAppAuthStrategy: Product = {
created_at: '2024-03-23T14:52:41.893Z',
updated_at: '2024-03-23T14:52:41.893Z',
id: '7b5f7176-bbb1-4e98-bb98-fa16ae492b64',
name: 'fooAPI',
description: null,
document_count: 0,
latest_version: {
id: versionWithOidcAuthStrategy.id,
name: versionWithOidcAuthStrategy.name
},
version_count: 1
}

const productVersion: ProductVersion = {
...versions[0],
registration_configs: []
Expand Down Expand Up @@ -91,6 +125,17 @@ const apps: GetApplicationResponse[] = [
}
]

export const appWithAuthStrategy: GetApplicationResponse = {
name: 'My Key-Auth App',
description: 'My DCR App has an auth strategy id',
reference_id: '5',
redirect_uri: 'http://google.com',
id: crypto.randomUUID(),
created_at: '2022-03-25T13:15:02.104Z',
updated_at: '2022-03-25T13:15:02.104Z',
auth_strategy_id: '452664ba-7d37-4e12-875c-3ca8044446fd'
}

const productRegistration: GetRegistrationResponse = {
created_at: '2022-03-25T13:15:02.104Z',
updated_at: '2022-03-25T13:15:02.104Z',
Expand Down
82 changes: 81 additions & 1 deletion cypress/e2e/specs/application_registration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CredentialCreationResponse, GetApplicationResponse, ListCredentialsResponse, ListCredentialsResponseDataInner, ListRegistrationsResponse } from '@kong/sdk-portal-js'
import { product, versions, productRegistration, apps, versionWithKeyAuthAuthStrategy, versionWithOidcAuthStrategy } from '../fixtures/consts'
import { product, versions, productRegistration, apps, productWithKeyAuthAppAuthStrategy, appWithAuthStrategy, versionWithKeyAuthAuthStrategy, versionWithOidcAuthStrategy } from '../fixtures/consts'

const mockApplicationWithCredAndReg = (
data: GetApplicationResponse,
Expand Down Expand Up @@ -673,6 +673,58 @@ describe('Application Registration', () => {
'You will be notified upon approval'
)
})
it('appregv2 - can request registration to a product version with app auth strategy id with feature flag enabled', () => {
cy.mockLaunchDarklyFlags([
{
name: 'tdx-3531-app-reg-v2',
value: true
}
])
cy.mockProductDocument(productWithKeyAuthAppAuthStrategy.id)
cy.mockProduct(productWithKeyAuthAppAuthStrategy.id, productWithKeyAuthAppAuthStrategy, [versionWithKeyAuthAuthStrategy])
cy.mockProductVersionApplicationRegistration(versionWithKeyAuthAuthStrategy)
cy.mockGetProductDocuments(productWithKeyAuthAppAuthStrategy.id)
cy.mockProductOperations(productWithKeyAuthAppAuthStrategy.id, versionWithKeyAuthAuthStrategy.id)
cy.mockProductVersionSpec(productWithKeyAuthAppAuthStrategy.id, versionWithKeyAuthAuthStrategy.id)
cy.mockRegistrations('*', []) // mock with empty so that we add one.

cy.viewport(1440, 900)
cy.visit(`/spec/${productWithKeyAuthAppAuthStrategy.id}`)
cy.get('.swagger-ui', { timeout: 12000 })

cy.mockApplications([appWithAuthStrategy], 1)
cy.mockProductVersionAvailableRegistrations(productWithKeyAuthAppAuthStrategy.id, versionWithKeyAuthAuthStrategy.id, [appWithAuthStrategy])
cy.mockGrantedScopes(versionWithKeyAuthAuthStrategy.id, appWithAuthStrategy.id, ['scope1', 'scope2'])
cy.wait('@getProductVersions')

cy.get('[data-testid="app-reg-v2-register-btn"]', { timeout: 12000 }).click()
cy.wait('@getProductVersionAvailableRegistrations').then(interception => {
// @ts-ignore the filter property is actually an object, but the type definition is wrong
expect(interception.request.query.filter?.auth_strategy_id).to.eq(versionWithKeyAuthAuthStrategy.registration_configs[0].id)
})
cy.get(selectors.appRegModal).should('exist')
cy.get(`${selectors.appRegModal} [data-testid="register-${appWithAuthStrategy.name}"]`).should('contain', appWithAuthStrategy.name).click()

const mockCreateRegResponse = {
...productRegistration,
status: 'pending',
application: appWithAuthStrategy
}

cy.intercept(
'POST',
`/api/v2/applications/${appWithAuthStrategy.id}/registrations*`,
{
body: mockCreateRegResponse
}
).as('postApplicationRegistration')

cy.get('[data-testid="submit-registration"]').click()
cy.get(selectors.appRegModal).should(
'contain',
'You will be notified upon approval'
)
})
it('appreg-v2 - feature flag off - does not show auth strategy card', () => {
cy.mockLaunchDarklyFlags([
{
Expand Down Expand Up @@ -751,6 +803,34 @@ describe('Application Registration', () => {
cy.get('[data-testid="register-button"]', { timeout: 12000 }).should('not.exist')
})

it('appregv2 - shows link to create new application if no applications match with feature flag enabled', () => {
cy.mockLaunchDarklyFlags([
{
name: 'tdx-3531-app-reg-v2',
value: true
}
])
cy.mockProductDocument(productWithKeyAuthAppAuthStrategy.id, versionWithKeyAuthAuthStrategy.id)
cy.mockProduct(productWithKeyAuthAppAuthStrategy.id, productWithKeyAuthAppAuthStrategy, [versionWithKeyAuthAuthStrategy])
cy.mockProductVersionApplicationRegistration(versionWithKeyAuthAuthStrategy)
cy.mockGetProductDocuments(productWithKeyAuthAppAuthStrategy.id)
cy.mockProductOperations(productWithKeyAuthAppAuthStrategy.id, versionWithKeyAuthAuthStrategy.id)
cy.mockProductVersionSpec(productWithKeyAuthAppAuthStrategy.id, versionWithKeyAuthAuthStrategy.id)
cy.mockRegistrations('*', []) // mock with empty so that we add one.

cy.viewport(1440, 900)
cy.visit(`/spec/${productWithKeyAuthAppAuthStrategy.id}`)
cy.get('.swagger-ui', { timeout: 12000 })

cy.mockApplications([], 0)
cy.mockProductVersionAvailableRegistrations(productWithKeyAuthAppAuthStrategy.id, versionWithKeyAuthAuthStrategy.id, [])

cy.get('[data-testid="app-reg-v2-register-btn"]', { timeout: 12000 }).click()
cy.get(selectors.appRegModal).should('exist')
cy.get(`${selectors.appRegModal} [data-testid="create-application"]`).should('exist')
cy.get(`${selectors.appRegModal} [data-testid="create-application"]`).should('have.prop', 'href').should('contain', `/application/create?product=${productWithKeyAuthAppAuthStrategy.id}&product_version=${versionWithKeyAuthAuthStrategy.id}&auth-strategy-id=${versionWithKeyAuthAuthStrategy.registration_configs[0].id}`)
})

it('does not show select available scopes if no scopes are available - feature flag on', () => {
cy.mockProductDocument()
cy.mockProduct()
Expand Down
4 changes: 2 additions & 2 deletions cypress/e2e/support/mock-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,11 @@ Cypress.Commands.add('mockProduct', (productId = '*', mockProduct = product, moc
}
}

cy.intercept('GET', `**/api/v2/products/${productId}/versions*`, {
cy.intercept('GET', `**/api/v2/products/${productId}/versions**`, {
statusCode: 200,
delay: 100,
body: versionsResponse
})
}).as('getProductVersions')

const productResponse: Product = {
...mockProduct
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"@kong-ui-public/spec-renderer": "0.13.5",
"@kong/kong-auth-elements": "2.11.1",
"@kong/kongponents": "8.127.0",
"@kong/sdk-portal-js": "2.6.1",
"@kong/sdk-portal-js": "2.7.0",
"@xstate/vue": "2.0.0",
"axios": "1.6.0",
"date-fns": "3.3.0",
Expand Down
42 changes: 35 additions & 7 deletions src/components/ViewSpecRegistrationModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
appearance="primary"
:disabled="currentState.matches('pending')"
class="button-spacing"
:to="{ name: 'create-application', query: { product: $route.params.product, product_version: $route.params.product_version } }"
:to="{ name: 'create-application', query: createApplicationQuery }"
>
{{ helpText.applicationRegistration.createApplication }}
</KButton>
Expand Down Expand Up @@ -118,13 +118,13 @@
</template>

<script lang="ts">
import { computed, defineComponent, ref, onMounted, watch } from 'vue'
import { computed, defineComponent, ref, onMounted, watch, PropType } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useMachine } from '@xstate/vue'
import { createMachine } from 'xstate'
import useToaster from '@/composables/useToaster'
import usePortalApi from '@/hooks/usePortalApi'
import { useI18nStore } from '@/stores'
import { ProductWithVersions, useI18nStore } from '@/stores'
import getMessageFromError from '@/helpers/getMessageFromError'
import useLDFeatureFlag from '@/hooks/useLDFeatureFlag'
import { FeatureFlags } from '@/constants/feature-flags'
Expand All @@ -142,7 +142,7 @@ export default defineComponent({
default: false
},
product: {
type: Object,
type: Object as PropType<ProductWithVersions>,
default: () => {}
},
version: {
Expand All @@ -166,6 +166,7 @@ export default defineComponent({
const selectedScopes = ref([])
const alreadyGrantedScopes = ref([])
const useDeveloperManagedScopes = useLDFeatureFlag(FeatureFlags.DeveloperManagedScopes, false)
const useAppRegV2 = useLDFeatureFlag(FeatureFlags.AppRegV2, false)
const applications = ref([])
const key = ref(0)
const fetchingScopes = ref(false)
Expand Down Expand Up @@ -238,6 +239,26 @@ export default defineComponent({
}[currentState.value.matches('success_application_status_is_pending') ? 'success' : 'default']
})
const authStrategyId = computed(() => {
const productVersion = $route.params.product_version
const matchingVersion = props.product.versions.find((version) => version.id === productVersion)
if (!matchingVersion) {
return
}
return matchingVersion.registration_configs[0]?.id
})
const createApplicationQuery = computed(() => {
return {
product: $route.params.product,
product_version: $route.params.product_version,
...(useAppRegV2 && authStrategyId.value)
? { 'auth-strategy-id': authStrategyId.value }
: {}
}
})
const rowAttrsFn = (rowItem) => {
return {
class: {
Expand All @@ -255,8 +276,13 @@ export default defineComponent({
const { pageSize, page: pageNumber } = payload
const requestOptions = {
productId: props.product?.id || $route.params.product,
productVersionId: props.version?.id || $route.params.product_version,
...(useAppRegV2
? {
filterAuthStrategyId: authStrategyId.value
}
: {}),
productId: props.product?.id || $route.params.product?.toString(),
productVersionId: props.version?.id || $route.params.product_version?.toString(),
...(searchStr.value.length && { filterNameContains: searchStr.value }),
unregistered: true,
pageNumber,
Expand Down Expand Up @@ -417,7 +443,9 @@ export default defineComponent({
alreadyRegisteredMessage,
handleRowClick,
submitSelection,
closeModal
closeModal,
authStrategyId,
createApplicationQuery
}
}
})
Expand Down
6 changes: 3 additions & 3 deletions src/views/Spec.vue
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
</template>
<script lang="ts">
import { defineComponent, computed, ref, watch, onMounted, toRaw, ComputedGetter } from 'vue'
import { defineComponent, computed, ref, watch, onMounted, toRaw, ComputedGetter, PropType } from 'vue'
import { storeToRefs } from 'pinia'
import { useRoute, useRouter } from 'vue-router'
import jsyaml from 'js-yaml'
Expand All @@ -121,7 +121,7 @@ import getMessageFromError from '@/helpers/getMessageFromError'
import ViewSpecModal from '@/components/ViewSpecModal.vue'
import ViewSpecRegistrationModal from '@/components/ViewSpecRegistrationModal.vue'
import usePortalApi from '@/hooks/usePortalApi'
import { useI18nStore, useAppStore, usePermissionsStore, useProductStore } from '@/stores'
import { useI18nStore, useAppStore, usePermissionsStore, useProductStore, ProductWithVersions } from '@/stores'
import { OperationListItem, SpecDetails } from '@kong-ui-public/spec-renderer'
import { idFromPathMethod } from '@/helpers/generatedOperationId'
import '@kong-ui-public/spec-renderer/dist/style.css'
Expand All @@ -138,7 +138,7 @@ export default defineComponent({
},
props: {
product: {
type: Object,
type: Object as PropType<ProductWithVersions>,
required: true
}
},
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1119,10 +1119,10 @@
v-calendar "3.0.0-alpha.8"
vue-draggable-next "^2.2.1"

"@kong/sdk-portal-js@2.6.1":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@kong/sdk-portal-js/-/sdk-portal-js-2.6.1.tgz#b855a505c0166825fcd4494f385a61bd7e5039e6"
integrity sha512-Q5Vcw57j48F+ujhGgtbTWyvshZBJgX/VKoZgEeRiJABAdfN7Td9CB+8UCAvK2uhk4kB0lzh/HCVqv9kCD1IhAQ==
"@kong/sdk-portal-js@2.7.0":
version "2.7.0"
resolved "https://registry.yarnpkg.com/@kong/sdk-portal-js/-/sdk-portal-js-2.7.0.tgz#8bacc5d27ff4c6ad656c48c809598b1f8d976cce"
integrity sha512-KrbIqStnE/43eGmfZpwhrnZ5f1P0oAxw0Ot4YmcyH9J0J+icyIoZBfBMZLexkeUUkWzE3Hjd0UsNlJ/0sf1yhg==
dependencies:
axios "1.6.0"

Expand Down

0 comments on commit e991472

Please sign in to comment.