Skip to content

Commit

Permalink
SALTO-4293 Update validateCredentials to identify whether the account…
Browse files Browse the repository at this point in the history
… is production or not (#4696)

* parse sandbox from URL

* added get license

* fixed broken test

* solved Omri suggestions

* modified test

* add more test

* changes tests

* resolved Omri suggestions

* resolved Omri's suggestions

* small fix

* small fix
  • Loading branch information
idoamit8 authored Aug 13, 2023
1 parent 9ee8a39 commit 22eb0bb
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 1 deletion.
22 changes: 21 additions & 1 deletion packages/jira-adapter/src/client/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { logger } from '@salto-io/logging'
import { AccountInfo, CredentialError } from '@salto-io/adapter-api'
import { client as clientUtils } from '@salto-io/adapter-components'
import { safeJsonStringify } from '@salto-io/adapter-utils'
import { Credentials } from '../auth'
import { FORCE_ACCEPT_LANGUAGE_HEADERS } from './headers'

const log = logger(module)

type appInfo = {
id: string
plan: string
}

const isAuthorized = async (
connection: clientUtils.APIConnection,
): Promise<boolean> => {
Expand All @@ -39,12 +48,23 @@ const getBaseUrl = async (
return response.data.baseUrl
}

/*
Based on the current implementation of the Jira API, we can't know if the account is a production
account, but in some cases we can know that it's not a production account.
*/
export const validateCredentials = async (
{ connection }: { connection: clientUtils.APIConnection },
): Promise<AccountInfo> => {
if (await isAuthorized(connection)) {
const accountId = await getBaseUrl(connection)
return { accountId }
if (accountId.includes('-sandbox-')) {
return { accountId, isProduction: false, accountType: 'Sandbox' }
}
const response = await connection.get('/rest/api/3/instance/license')
log.info(`Jira application's info: ${safeJsonStringify(response.data.applications)}`)
const hasPaidApp = response.data.applications.some((app: appInfo) => app.plan === 'PAID')
const isProduction = hasPaidApp ? undefined : false
return { accountId, isProduction }
}
throw new CredentialError('Invalid Credentials')
}
Expand Down
1 change: 1 addition & 0 deletions packages/jira-adapter/test/adapter_creator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ describe('adapter creator', () => {
describe('with valid credentials', () => {
let accountId: string
beforeEach(async () => {
mockAxiosAdapter.onGet('/rest/api/3/instance/license').reply(200, { applications: [{ plan: 'FREE' }] })
mockAxiosAdapter.onGet().reply(200, { baseUrl: 'http://my_account.net' });
({ accountId } = await adapter.validateCredentials(
createCredentialsInstance({ baseUrl: 'http://my.net', user: 'u', token: 't' })
Expand Down
51 changes: 51 additions & 0 deletions packages/jira-adapter/test/client/connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describe('connection', () => {
mockAxios = new MockAdapter(axios)
mockAxios.onGet('/rest/api/3/configuration').reply(200)
mockAxios.onGet('/rest/api/3/serverInfo').reply(200, { baseUrl: 'http://my.jira.net' })
mockAxios.onGet('/rest/api/3/instance/license').reply(200, { applications: [{ plan: 'FREE' }] })
connection = await createConnection({ retries: 1 }).login(
{ baseUrl: 'http://myJira.net', user: 'me', token: 'tok', isDataCenter: false }
)
Expand Down Expand Up @@ -78,6 +79,7 @@ describe('connection', () => {
mockAxios = new MockAdapter(axios)
mockAxios.onGet('/rest/api/3/configuration').reply(200)
mockAxios.onGet('/rest/api/3/serverInfo').reply(200, { baseUrl: 'http://my.jira.net' })
mockAxios.onGet('/rest/api/3/instance/license').reply(200, { applications: [{ plan: 'FREE' }] })
connection = await createConnection({ retries: 1 }).login(
{ baseUrl: 'http://myJira.net', user: 'me', token: 'tok', isDataCenter: false }
)
Expand All @@ -102,6 +104,7 @@ describe('connection', () => {
mockAxios = new MockAdapter(axios)
mockAxios.onGet('/rest/api/3/configuration').reply(200)
mockAxios.onGet('/rest/api/3/serverInfo').reply(200, { baseUrl: 'http://my.jira.net' })
mockAxios.onGet('/rest/api/3/instance/license').reply(200, { applications: [{ plan: 'FREE' }] })
connection = await createConnection({ retries: 1 }).login(
{ baseUrl: 'http://myJira.net', user: 'me', token: 'tok', isDataCenter: true }
)
Expand All @@ -115,9 +118,57 @@ describe('connection', () => {

it('should not have force accept language headers when calling Jira DC', async () => {
mockAxios.onGet('/rest/api/3/serverInfo').reply(200, { baseUrl: 'http://my.jira.net' })
mockAxios.onGet('/rest/api/3/instance/license').reply(200, { applications: [{ plan: 'FREE' }] })
expect(mockAxios.history.get).toContainEqual(expect.objectContaining({
headers: expect.not.objectContaining(FORCE_ACCEPT_LANGUAGE_HEADERS),
}))
})
})
describe('validate isProduction', () => {
let mockAxios: MockAdapter
let connection: clientUtils.APIConnection
beforeEach(async () => {
mockAxios = new MockAdapter(axios)
mockAxios.onGet('/rest/api/3/configuration').reply(200)
})
afterEach(() => {
mockAxios.restore()
})
it('should return isProduction undefined and accountType = undefined when account id does not include -sandbox- and has paid app', async () => {
connection = await createConnection({ retries: 1 }).login(
{ baseUrl: 'http://myJira.net', user: 'me', token: 'tok', isDataCenter: true }
)
mockAxios.onGet('/rest/api/3/serverInfo').reply(200, { baseUrl: 'http://my.jira.net' })
mockAxios.onGet('/rest/api/3/instance/license').reply(200, { applications: [{ id: 'software', plan: 'PAID' }, { id: 'serviceDesk', plan: 'FREE' }] })
const { isProduction, accountType } = await validateCredentials({
connection,
})
expect(isProduction).toEqual(undefined)
expect(accountType).toEqual(undefined)
})
it('should return isProduction false and accountType = "Sandbox" when account id includes -sandbox-', async () => {
connection = await createConnection({ retries: 1 }).login(
{ baseUrl: 'https://test-sandbox-999.atlassian.net', user: 'me', token: 'tok', isDataCenter: true }
)
mockAxios.onGet('/rest/api/3/serverInfo').reply(200, { baseUrl: 'https://test-sandbox-999.atlassian.net' })
mockAxios.onGet('/rest/api/3/instance/license').reply(200, { applications: [{ plan: 'PAID' }] })
const { isProduction, accountType } = await validateCredentials({
connection,
})
expect(isProduction).toEqual(false)
expect(accountType).toEqual('Sandbox')
})
it('should return isProduction false and accountType = undefined when account id does not include -sandbox- but has no paid app', async () => {
connection = await createConnection({ retries: 1 }).login(
{ baseUrl: 'https://test-sandbox-999.atlassian.net', user: 'me', token: 'tok', isDataCenter: true }
)
mockAxios.onGet('/rest/api/3/serverInfo').reply(200, { baseUrl: 'https://test.atlassian.net' })
mockAxios.onGet('/rest/api/3/instance/license').reply(200, { applications: [{ plan: 'FREE' }] })
const { isProduction, accountType } = await validateCredentials({
connection,
})
expect(accountType).toEqual(undefined)
expect(isProduction).toEqual(false)
})
})
})

0 comments on commit 22eb0bb

Please sign in to comment.