Skip to content

Commit

Permalink
feat: #182 Add redirect uri option in developer portal (#211)
Browse files Browse the repository at this point in the history
  • Loading branch information
trankhacvy authored Feb 10, 2020
1 parent 07e67fd commit 30d35b3
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export interface AppDetailModel {
* Gets the Uri at which the app is launched
*/
launchUri?: string
/**
* Gets the app revisions redirect uri (or uris) where a user will be redirected to immediately after a successful authentication
*/
redirectUris?: string[]
/**
* Gets the date the app was installed for a specific client
*/
Expand Down Expand Up @@ -349,6 +353,10 @@ export interface CreateAppModel {
* Sets the apps launch uri
*/
launchUri?: string
/**
* Sets the apps uri where a user will be redirected to immediately after a successful authentication. Multiple URIs can be passed as a comma separated list
*/
redirectUris?: string[]
/**
* Sets the unique identifer of the developer registering the app
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ exports[`DeveloperSubmitApp should match a snapshot 1`] = `
"iconImageUrl": "",
"launchUri": "",
"name": "",
"redirectUris": "",
"scopes": Array [],
"screen1ImageUrl": "",
"screen2ImageUrl": "",
Expand Down Expand Up @@ -85,6 +86,7 @@ exports[`DeveloperSubmitApp should match submit revision form snapshot 1`] = `
"isListed": undefined,
"launchUri": undefined,
"name": "Peter's Properties",
"redirectUris": "",
"scopes": Array [
"Marketplace/developers.read",
"Marketplace/developers.write",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
handleCloseModal,
handleAcceptTerms,
handleDeclineTerms,
CustomCreateAppModel,
} from '../developer-submit-app'
import { appDetailDataStub } from '../../../sagas/__stubs__/app-detail'
import { appCategorieStub } from '../../../sagas/__stubs__/app-categories'
Expand Down Expand Up @@ -415,7 +416,7 @@ describe('handleSubmitApp', () => {
isAgreedTerms: false,
setShouldShowError: jest.fn(),
}
const appModel = {}
const appModel = { redirectUris: '' } as CustomCreateAppModel
const actions = {} as any
afterEach(() => jest.clearAllMocks())
it('should call setShouldShowError when not agree', () => {
Expand Down
37 changes: 30 additions & 7 deletions packages/marketplace/src/components/pages/developer-submit-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import { selectCategories } from '../../selector/app-categories'
import styles from '@/styles/pages/developer-submit-app.scss?mod'
import { TermsAndConditionsModal } from '../ui/terms-and-conditions-modal'

export type CustomCreateAppModel = Omit<CreateAppModel, 'redirectUris'> & { redirectUris?: string }

export interface SubmitAppMappedActions {
submitApp: (
appModel: CreateAppModel,
Expand Down Expand Up @@ -99,6 +101,7 @@ export const generateInitialValues = (appDetail: AppDetailModel | null, develope
isListed,
isDirectApi,
scopes: appScopes,
redirectUris = [],
} = appDetail

const icon = (media || []).filter(({ order }) => order === 0)[0]
Expand All @@ -124,6 +127,7 @@ export const generateInitialValues = (appDetail: AppDetailModel | null, develope
isListed,
isDirectApi,
scopes: appScopes ? appScopes.map(item => item.name) : [],
redirectUris: redirectUris.join(','),
...images,
}
} else {
Expand All @@ -145,6 +149,7 @@ export const generateInitialValues = (appDetail: AppDetailModel | null, develope
summary: '',
developerId,
scopes: [],
redirectUris: '',
}
}

Expand All @@ -158,18 +163,22 @@ export const handleSubmitApp = ({
setSubmitError,
isAgreedTerms,
setShouldShowError,
}) => (appModel: CreateAppModel, actions: FormikHelpers<CreateAppModel>) => {
}) => (appModel: CustomCreateAppModel, actions: FormikHelpers<CustomCreateAppModel>) => {
if (!isAgreedTerms) {
setShouldShowError(true)
return
}
if (!appId) {
submitApp(appModel, actions, setSubmitError)
submitApp(
{ ...appModel, redirectUris: appModel.redirectUris ? appModel.redirectUris.split(',') : [] },
actions,
setSubmitError,
)
} else {
if (appModel.authFlow) {
delete appModel.authFlow
}
submitRevision(appId, appModel)
submitRevision(appId, { ...appModel, redirectUris: appModel.redirectUris ? appModel.redirectUris.split(',') : [] })
}
}

Expand Down Expand Up @@ -366,15 +375,28 @@ export const SubmitApp: React.FC<SubmitAppProps> = ({
/>
</GridItem>
</Grid>
<Grid>
<GridItem>
<Input
dataTest="submit-app-redirect-uri"
type="text"
labelText="Redirect URI(s)"
id="redirectUris"
name="redirectUris"
placeholder="Enter your callback URI’s. For multiple URI's, separate using a comma. HTTPS only other than for http://localhost"
/>
</GridItem>
</Grid>
<Grid>
<GridItem>
<TextArea
id="summary"
dataTest="submit-app-summary"
labelText="Summary"
name="summary"
placeholder={`A short strapline summary for your app
listing. Must be between 50 and 150 characters`}
placeholder={
'A short strapline summary for your app listing. Must be between 50 and 150 characters'
}
/>
</GridItem>
<GridItem>
Expand All @@ -383,8 +405,9 @@ export const SubmitApp: React.FC<SubmitAppProps> = ({
dataTest="submit-app-description"
labelText="Description"
name="description"
placeholder={`A detailed description for your app listing.
Must be between 150 and 1000 characters`}
placeholder={
'A detailed description for your app listing. Must be between 150 and 1000 characters'
}
/>
</GridItem>
</Grid>
Expand Down
2 changes: 1 addition & 1 deletion packages/marketplace/src/tests/badges/badge-branches.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/marketplace/src/tests/badges/badge-functions.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/marketplace/src/tests/badges/badge-lines.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 25 additions & 1 deletion packages/marketplace/src/utils/__tests__/validate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isEmail } from '../validate'
import { isEmail, isValidHttpsUrl, isValidRedirectUrls } from '../validate'

describe('isEmail', () => {
it('valid email test', () => {
Expand Down Expand Up @@ -31,3 +31,27 @@ describe('isEmail', () => {
].forEach(email => expect(isEmail(email)).toBeFalsy())
})
})

describe('isValidHttpsUrl', () => {
it('valid https url test', () => {
;['https://www.google.com', 'https://www.googlee.com'].forEach(url => expect(isValidHttpsUrl(url)).toBeTruthy())
})

it('invalid https url test', () => {
;['http://google.com', 'htt://www.google.com'].forEach(url => expect(isValidHttpsUrl(url)).toBeFalsy())
})
})

describe('isValidRedirectUrls', () => {
it('valid redirect url test', () => {
;['https://www.google.com,http://localhost:8080', 'https://www.googlee.com,https://www.facebook.com'].forEach(url =>
expect(isValidRedirectUrls(url)).toBeTruthy(),
)
})

it('invalid https url test', () => {
;['http://google.com,htt:google.ck', 'htt://www.google.com,ftp://www.google.com'].forEach(url =>
expect(isValidRedirectUrls(url)).toBeFalsy(),
)
})
})
35 changes: 31 additions & 4 deletions packages/marketplace/src/utils/form/__tests__/submit-app.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import errorMessages from '@/constants/error-messages'
import { CreateAppModel } from '@reapit/foundations-ts-definitions'
import { CustomCreateAppModel } from '@/components/pages/developer-submit-app'
import { validate } from '../submit-app'

describe('submitAppValidation', () => {
it('validate require all field', () => {
const input: CreateAppModel = {
const input: CustomCreateAppModel = {
screen4ImageUrl: '',
screen3ImageUrl: '',
screen2ImageUrl: '',
Expand All @@ -18,6 +18,7 @@ describe('submitAppValidation', () => {
description: '',
summary: '',
scopes: [],
redirectUris: '',
}

const validateRequiredKeys = [
Expand All @@ -30,6 +31,7 @@ describe('submitAppValidation', () => {
'description',
'screen1ImageUrl',
'summary',
'redirectUris',
]

const output = {}
Expand All @@ -41,7 +43,7 @@ describe('submitAppValidation', () => {
})

it('validate email field support email', () => {
const input: CreateAppModel = {
const input: CustomCreateAppModel = {
screen4ImageUrl: 'test',
screen3ImageUrl: 'test',
screen2ImageUrl: 'test',
Expand All @@ -55,15 +57,39 @@ describe('submitAppValidation', () => {
description: 'test',
summary: 'test',
scopes: [],
redirectUris: 'https://google.com,https://twitter.com,http://localhost:8080',
}

expect(validate(input)).toEqual({
supportEmail: errorMessages.FIELD_WRONG_EMAIL_FORMAT,
})
})

it('validate redirect uri(s) field ', () => {
const input: CustomCreateAppModel = {
screen4ImageUrl: 'test',
screen3ImageUrl: 'test',
screen2ImageUrl: 'test',
screen1ImageUrl: 'test',
name: 'test',
telephone: 'test',
supportEmail: '[email protected]',
launchUri: 'test',
iconImageUrl: 'test',
homePage: 'test',
description: 'test',
summary: 'test',
scopes: [],
redirectUris: 'http://google.com,https://twitter.com,http://localhost:8080',
}

expect(validate(input)).toEqual({
redirectUris: 'Invalid redirect uri(s)',
})
})

it('return empty object it everything is valid', () => {
const input: CreateAppModel = {
const input: CustomCreateAppModel = {
screen4ImageUrl: 'test',
screen3ImageUrl: 'test',
screen2ImageUrl: 'test',
Expand All @@ -77,6 +103,7 @@ describe('submitAppValidation', () => {
description: 'test',
summary: 'test',
scopes: [],
redirectUris: 'https://google.com,https://twitter.com,http://localhost:8080',
}

expect(validate(input)).toEqual({})
Expand Down
13 changes: 10 additions & 3 deletions packages/marketplace/src/utils/form/submit-app.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CreateAppModel } from '@reapit/foundations-ts-definitions'
import { validateRequire, validateEmail } from '@reapit/elements'
import { CustomCreateAppModel } from '@/components/pages/developer-submit-app'
import { isValidRedirectUrls } from '@/utils/validate'

export type SubmitAppFormErrorKeys =
| 'name'
Expand All @@ -12,9 +13,10 @@ export type SubmitAppFormErrorKeys =
| 'summary'
| 'screen1ImageUrl'
| 'authFlow'
| 'redirectUris'

export const validate = (values: CreateAppModel) => {
let errors = validateRequire<CreateAppModel, SubmitAppFormErrorKeys>({
export const validate = (values: CustomCreateAppModel) => {
let errors = validateRequire<CustomCreateAppModel, SubmitAppFormErrorKeys>({
values,
currentErrors: {},
keys: [
Expand All @@ -28,6 +30,7 @@ export const validate = (values: CreateAppModel) => {
'summary',
'screen1ImageUrl',
'authFlow',
'redirectUris',
],
})

Expand All @@ -37,5 +40,9 @@ export const validate = (values: CreateAppModel) => {
keys: ['supportEmail'],
})

if (values.redirectUris && !isValidRedirectUrls(values.redirectUris)) {
errors.redirectUris = 'Invalid redirect uri(s)'
}

return errors
}
11 changes: 11 additions & 0 deletions packages/marketplace/src/utils/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,14 @@ export function isEmail(email: string) {
const pattern = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i
return pattern.test(email)
}

export function isValidHttpsUrl(url: string) {
return /^\s*(https:\/\/)([a-z\d-]{1,63}\.)*[a-z\d-]{1,255}\.[a-z]{2,6}\s*/.test(url)
}

export function isValidRedirectUrls(urls: string) {
return urls
.split(',')
.filter(url => !!url)
.every(url => isValidHttpsUrl(url) || /http?:\/\/localhost/.test(url))
}

0 comments on commit 30d35b3

Please sign in to comment.