-
Notifications
You must be signed in to change notification settings - Fork 84
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
Feature: list all permissions #477
Changes from 13 commits
d0d1839
07adce7
2deec61
f5edc61
a00a1d0
47cc05e
dd8535c
7db0860
4c6a3ab
9de0bea
9664111
0a15130
02afbbc
cb6446e
a7f980b
165b478
2602b66
f63da01
ff47246
e05270b
3f27fc2
a3e27a5
409905e
926c0f2
7f44b27
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,10 @@ | ||
import { IAction } from '../../../types/action'; | ||
import { IQuery } from '../../../types/query-runner'; | ||
import { IRequestOptions } from '../../../types/request'; | ||
import { FETCH_SCOPES_ERROR, FETCH_SCOPES_SUCCESS } from '../redux-constants'; | ||
import { parseSampleUrl } from '../../utils/sample-url-generation'; | ||
import { acquireNewAccessToken } from '../graph-client/msal-service'; | ||
import { FETCH_SCOPES_ERROR, FETCH_SCOPES_PENDING, FETCH_SCOPES_SUCCESS } from '../redux-constants'; | ||
import { getAuthTokenSuccess, getConsentedScopesSuccess } from './auth-action-creators'; | ||
|
||
export function fetchScopesSuccess(response: object): IAction { | ||
return { | ||
|
@@ -9,47 +13,60 @@ export function fetchScopesSuccess(response: object): IAction { | |
}; | ||
} | ||
|
||
export function fetchScopesPending(): any { | ||
return { | ||
type: FETCH_SCOPES_PENDING, | ||
}; | ||
} | ||
|
||
export function fetchScopesError(response: object): IAction { | ||
return { | ||
type: FETCH_SCOPES_ERROR, | ||
response, | ||
}; | ||
} | ||
|
||
export function fetchScopes(): Function { | ||
export function fetchScopes(query?: IQuery): Function { | ||
return async (dispatch: Function, getState: Function) => { | ||
const { sampleQuery: { sampleUrl, selectedVerb } } = getState(); | ||
const urlObject: URL = new URL(sampleUrl); | ||
const createdAt = new Date().toISOString(); | ||
// remove the prefix i.e. beta or v1.0 and any possible extra '/' character at the end | ||
const requestUrl = urlObject.pathname.substr(5).replace(/\/$/, ''); | ||
const permissionsUrl = 'https://graphexplorerapi.azurewebsites.net/api/GraphExplorerPermissions?requesturl=' + | ||
requestUrl + '&method=' + selectedVerb; | ||
|
||
const headers = { | ||
'Content-Type': 'application/json', | ||
}; | ||
|
||
const options: IRequestOptions = { headers }; | ||
|
||
return fetch(permissionsUrl, options) | ||
.then(res => res.json()) | ||
.then(res => { | ||
if (res.error) { | ||
throw (res.error); | ||
try { | ||
const { devxApi } = getState(); | ||
thewahome marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let permissionsUrl = `${devxApi}/permissions`; | ||
|
||
if (query) { | ||
const { requestUrl, sampleUrl } = parseSampleUrl(query.sampleUrl); | ||
|
||
if (!sampleUrl) { | ||
throw new Error('url is invalid'); | ||
} | ||
dispatch(fetchScopesSuccess(res)); | ||
}) | ||
.catch(() => { | ||
const duration = (new Date()).getTime() - new Date(createdAt).getTime(); | ||
const response = { | ||
/* Return 'Forbidden' regardless of error, as this was a | ||
permission-centric operation with regards to user context */ | ||
statusText: 'Forbidden', | ||
status: '403', | ||
duration | ||
}; | ||
return dispatch(fetchScopesError(response)); | ||
}); | ||
|
||
permissionsUrl = `${permissionsUrl}?requesturl=/${requestUrl}&method=${query.selectedVerb}`; | ||
} | ||
|
||
const headers = { | ||
'Content-Type': 'application/json', | ||
}; | ||
const options: IRequestOptions = { headers }; | ||
|
||
dispatch(fetchScopesPending()); | ||
|
||
const response = await fetch(permissionsUrl, options); | ||
if (response.ok) { | ||
const scopes = await response.json(); | ||
return dispatch(fetchScopesSuccess(scopes)); | ||
} | ||
throw (response); | ||
} catch (error) { | ||
return dispatch(fetchScopesError(error)); | ||
} | ||
}; | ||
} | ||
|
||
export function consentToScopes(scopes: string[]): Function { | ||
return async (dispatch: Function) => { | ||
const authResponse = await acquireNewAccessToken(scopes); | ||
if (authResponse && authResponse.accessToken) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to have an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jobala I want to say there is :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets not repeat ourselves |
||
dispatch(getAuthTokenSuccess(authResponse.accessToken)); | ||
dispatch(getConsentedScopesSuccess(authResponse.scopes)); | ||
} | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,7 +25,7 @@ export function fetchSamplesPending(): any { | |
export function fetchSamples(): Function { | ||
return async (dispatch: Function, getState: Function) => { | ||
const devxApi = getState().devxApi; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: Use object destructuring |
||
const samplesUrl = `${devxApi}/api/GraphExplorerSamples`; | ||
const samplesUrl = `${devxApi}/samples`; | ||
|
||
const headers = { | ||
'Content-Type': 'application/json', | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
import { IAction } from '../../../types/action'; | ||
import { FETCH_SCOPES_ERROR, FETCH_SCOPES_SUCCESS } from '../redux-constants'; | ||
import { FETCH_SCOPES_ERROR, FETCH_SCOPES_PENDING, FETCH_SCOPES_SUCCESS } from '../redux-constants'; | ||
|
||
const initialState = { | ||
pending: false, | ||
|
@@ -11,15 +11,21 @@ export function scopes(state = initialState, action: IAction): any { | |
switch (action.type) { | ||
case FETCH_SCOPES_SUCCESS: | ||
return { | ||
...state, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
wouldn't this still be good to have in case the state object ever adds new items. |
||
pending: false, | ||
data: action.response | ||
data: action.response, | ||
error: null | ||
}; | ||
case FETCH_SCOPES_ERROR: | ||
return { | ||
...state, | ||
pending: false, | ||
error: action.response | ||
error: action.response, | ||
data: [] | ||
}; | ||
case FETCH_SCOPES_PENDING: | ||
return { | ||
pending: true, | ||
data: [], | ||
error: null | ||
}; | ||
default: | ||
return state; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,31 @@ | ||
import { IAction } from '../../../types/action'; | ||
import { GET_SNIPPET_SUCCESS } from '../redux-constants'; | ||
import { GET_SNIPPET_ERROR, GET_SNIPPET_PENDING, GET_SNIPPET_SUCCESS } from '../redux-constants'; | ||
|
||
export function snippets(state = {}, action: IAction): any { | ||
const initialState = { | ||
pending: false, | ||
data: {}, | ||
error: null | ||
}; | ||
export function snippets(state = initialState, action: IAction): any { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
great place to use typescript to ensure the type coming out is the state type. #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can create a base type for all the initial states that involve fetching data from APIs other than graph can inherit from so that it can be consistent, This is some chunk of work which will make this PR a headache to review. I can add this to the backlog as technical debt... #Resolved |
||
switch (action.type) { | ||
case GET_SNIPPET_SUCCESS: | ||
return { ...state, ...action.response as object }; | ||
return { | ||
pending: false, | ||
data: action.response as object, | ||
error: null | ||
}; | ||
case GET_SNIPPET_ERROR: | ||
return { | ||
pending: false, | ||
data: null, | ||
error: action.response as object | ||
}; | ||
case GET_SNIPPET_PENDING: | ||
return { | ||
pending: true, | ||
data: null, | ||
error: null | ||
}; | ||
default: | ||
return state; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { SortOrder } from '../../types/enums'; | ||
/** | ||
* Sorts a given array by the passed in property in the direction specified | ||
* @param {string} property the property to sort the array with | ||
* @param {SortOrder} sortOrder the direction to follow Ascending / Descending | ||
* You pass this helper to the array sort function | ||
*/ | ||
export function dynamicSort(property: string, sortOrder: SortOrder) { | ||
let order = 1; | ||
if (sortOrder === SortOrder.DESC) { | ||
order = -1; | ||
} | ||
return (first: any, second: any) => { | ||
const result = (first[property] < second[property]) ? -1 : (first[property] > second[property]) ? 1 : 0; | ||
return result * order; | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i know you don't like creating an interface for something this simple but it really does help keep the code more readable and maintainable.