replace parse-link-header
it had a node dependency which required a webpack fallback from querystring to querystring-es3 flag=none Change-Id: Ib403113f41990c279e2192662b780be10f744853 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/336059 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Michael Hulse <michael.hulse@instructure.com> QA-Review: Aaron Shafovaloff <ashafovaloff@instructure.com> Product-Review: Aaron Shafovaloff <ashafovaloff@instructure.com>
This commit is contained in:
parent
d21933407f
commit
bb3a0d5d60
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import {combineReducers} from 'redux'
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import parseLinkHeader from '@canvas/parse-link-header'
|
||||
import initialState from '../store/initialState'
|
||||
|
||||
const emailRegex = /([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})/i
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import parseLinkHeader from '@canvas/parse-link-header'
|
||||
import ajaxJSON from '@canvas/jquery/jquery.ajaxJSON'
|
||||
import createStore from '@canvas/backbone/createStore'
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ import $ from 'jquery'
|
|||
import {useScope as useI18nScope} from '@canvas/i18n'
|
||||
import Spinner from 'spin.js'
|
||||
import htmlEscape from '@instructure/html-escape'
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import parseLinkHeader from '@canvas/parse-link-header'
|
||||
|
||||
const I18n = useI18nScope('paginated_list')
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import axios from '@canvas/axios'
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import parseLinkHeader from '@canvas/parse-link-header'
|
||||
import numberHelper from '@canvas/i18n/numberHelper'
|
||||
|
||||
import categories, {OTHER_ID} from './categories'
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
|
||||
import getCookie from '@instructure/get-cookie'
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import parseLinkHeader from '@canvas/parse-link-header'
|
||||
import {defaultFetchOptions} from '@canvas/util/xhr'
|
||||
import {toQueryString} from '@canvas/query-string-encoding'
|
||||
import type {QueryParameterRecord} from '@canvas/query-string-encoding'
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import _ from 'lodash'
|
||||
import uuid from 'uuid'
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import parseLinkHeader from '@canvas/parse-link-header'
|
||||
import NaiveFetchDispatch from './NaiveFetchDispatch'
|
||||
import makePromisePool from '@canvas/make-promise-pool'
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import parseLinkHeader from '@canvas/parse-link-header'
|
||||
|
||||
import NetworkFake from '../../NetworkFake'
|
||||
import {sendGetRequest} from '../../specHelpers'
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import {find, isArray} from 'lodash'
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import parseLinkHeader from '@canvas/parse-link-header'
|
||||
import deferPromise from '@instructure/defer-promise'
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (C) 2023 - present Instructure, Inc.
|
||||
*
|
||||
* This file is part of Canvas.
|
||||
*
|
||||
* Canvas is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import parseLinkHeader from '../parseLinkHeader'
|
||||
|
||||
const linkHeader =
|
||||
'<https://www.example.com/path?page=2&per_page=10>; rel="next", ' +
|
||||
'<https://www.example.com/path?page=1&per_page=10>; rel="prev"; foo="bar", ' +
|
||||
'<https://www.example.com/path?page=5&per_page=10>; rel="last"'
|
||||
|
||||
describe('parseLinkHeader', () => {
|
||||
it('should parse a link header', () => {
|
||||
const parsed = parseLinkHeader(linkHeader)
|
||||
expect(parsed).toEqual({
|
||||
next: {
|
||||
page: '2',
|
||||
per_page: '10',
|
||||
rel: 'next',
|
||||
url: 'https://www.example.com/path?page=2&per_page=10',
|
||||
},
|
||||
prev: {
|
||||
page: '1',
|
||||
per_page: '10',
|
||||
foo: 'bar',
|
||||
rel: 'prev',
|
||||
url: 'https://www.example.com/path?page=1&per_page=10',
|
||||
},
|
||||
last: {
|
||||
page: '5',
|
||||
per_page: '10',
|
||||
rel: 'last',
|
||||
url: 'https://www.example.com/path?page=5&per_page=10',
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "@canvas/parse-link-header",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"author": "neme",
|
||||
"main": "./parseLinkHeader.ts"
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (C) 2023 - present Instructure, Inc.
|
||||
*
|
||||
* This file is part of Canvas.
|
||||
*
|
||||
* Canvas is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Affero General Public License as published by the Free
|
||||
* Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// based on https://github.com/thlorenz/parse-link-header/blob/master/index.js (MIT)
|
||||
|
||||
type LinkInfo = {
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
function parseQueryParams(linkUrl: string): {[key: string]: string} {
|
||||
const queryParams: {[key: string]: string} = {}
|
||||
const urlParts = linkUrl.split('?')
|
||||
if (urlParts.length > 1) {
|
||||
urlParts[1].split('&').forEach(param => {
|
||||
const [key, value] = param.split('=')
|
||||
queryParams[key] = decodeURIComponent(value)
|
||||
})
|
||||
}
|
||||
return queryParams
|
||||
}
|
||||
|
||||
function parseLink(link: string): LinkInfo | null {
|
||||
try {
|
||||
const linkMatch = link.match(/<([^>]*)>\s*(.*)/)
|
||||
if (!linkMatch) {
|
||||
return null
|
||||
}
|
||||
|
||||
const [, linkUrl, partsString] = linkMatch
|
||||
const parts = partsString.split(';').map(part => part.trim())
|
||||
|
||||
const info: LinkInfo = {url: linkUrl}
|
||||
|
||||
parts.forEach(part => {
|
||||
const partMatch = part.match(/(.+)\s*=\s*"?([^"]+)"?/)
|
||||
if (partMatch) {
|
||||
const [, key, value] = partMatch
|
||||
info[key.trim()] = value.trim()
|
||||
}
|
||||
})
|
||||
|
||||
return {...parseQueryParams(linkUrl), ...info}
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function hasRel(x: LinkInfo | null): x is LinkInfo {
|
||||
return x !== null && 'rel' in x
|
||||
}
|
||||
|
||||
function intoRels(acc: {[rel: string]: LinkInfo}, x: LinkInfo): {[rel: string]: LinkInfo} {
|
||||
x.rel.split(/\s+/).forEach(rel => {
|
||||
const {...rest} = x
|
||||
acc[rel] = rest
|
||||
})
|
||||
|
||||
return acc
|
||||
}
|
||||
|
||||
const PARSE_LINK_HEADER_MAXLEN = 2000
|
||||
const PARSE_LINK_HEADER_THROW_ON_MAXLEN_EXCEEDED =
|
||||
process.env.PARSE_LINK_HEADER_THROW_ON_MAXLEN_EXCEEDED != null
|
||||
|
||||
function checkHeader(linkHeader: string | undefined): boolean {
|
||||
if (!linkHeader) return false
|
||||
|
||||
if (linkHeader.length > PARSE_LINK_HEADER_MAXLEN) {
|
||||
if (PARSE_LINK_HEADER_THROW_ON_MAXLEN_EXCEEDED) {
|
||||
throw new Error(
|
||||
`Input string too long, it should be under ${PARSE_LINK_HEADER_MAXLEN} characters.`
|
||||
)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export default function parseLinkHeader(linkHeader: string): {[rel: string]: LinkInfo} | null {
|
||||
if (!checkHeader(linkHeader)) return null
|
||||
|
||||
return linkHeader
|
||||
.split(/,\s*(?=<)/)
|
||||
.map(parseLink)
|
||||
.filter(hasRel)
|
||||
.reduce(intoRels, {})
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
import {createActions, createAction} from 'redux-actions'
|
||||
import axios from 'axios'
|
||||
import {asAxios, getPrefetchedXHR} from '@canvas/util/xhr'
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import parseLinkHeader from '@canvas/parse-link-header'
|
||||
import configureAxios from '../utilities/configureAxios'
|
||||
import {alert} from '../utilities/alertUtils'
|
||||
import {useScope as useI18nScope} from '@canvas/i18n'
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import axios from 'axios'
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import parseLinkHeader from '@canvas/parse-link-header'
|
||||
import {put, select, call, all, takeEvery} from 'redux-saga/effects'
|
||||
import {getFirstLoadedMoment, getLastLoadedMoment} from '../utilities/dateUtils'
|
||||
import {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
import moment from 'moment-timezone'
|
||||
import _ from 'lodash'
|
||||
import parseLinkHeader from 'parse-link-header'
|
||||
import parseLinkHeader from '@canvas/parse-link-header'
|
||||
|
||||
const getItemDetailsFromPlannable = apiResponse => {
|
||||
const {plannable, plannable_type, planner_override} = apiResponse
|
||||
|
|
Loading…
Reference in New Issue