-
Notifications
You must be signed in to change notification settings - Fork 13
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
feat(hub-common): allow force updates of cache files as well as cache status reporting #1657
Open
sonofflynn89
wants to merge
4
commits into
master
Choose a base branch
from
f/11445-add-cachestatus-and-updatecache
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+273
−29
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
d02f44c
feat(hub-common): add first pass at simulating updating download cach…
sonofflynn89 86521b2
feat(hub-common): add real cacheStatus detection and fix download-api…
sonofflynn89 6b92bba
feat(hub-common): export DownloadCacheStatus type and add code comments
sonofflynn89 2e8f3dd
test(hub-common): add tests for cacheStatus and updateCache
sonofflynn89 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ import HubError from "../../../HubError"; | |
import { getProp } from "../../../objects/get-prop"; | ||
import { | ||
ArcgisHubDownloadError, | ||
DownloadCacheStatus, | ||
DownloadOperationStatus, | ||
IFetchDownloadFileOptions, | ||
IFetchDownloadFileResponse, | ||
|
@@ -11,7 +12,8 @@ import { | |
|
||
/** | ||
* @private | ||
* Fetches a download file url from the Hub Download API | ||
* Fetches a download file url from the Hub Download API. If we know the file will be served from | ||
* a cache, we also return the status of the cache. | ||
* | ||
* NOTE: The Hub Download API only works with a certain subset of Feature and Map services | ||
* and performs different operations (i.e., calling createReplica or paging the service's | ||
|
@@ -21,15 +23,29 @@ import { | |
* interface for downloading data from any service supported by the Hub Download API. | ||
* | ||
* @param options options for refining / filtering the resulting download file | ||
* @returns a url to download the file | ||
* @returns the url to download the file and cache status | ||
*/ | ||
export async function fetchHubApiDownloadFile( | ||
options: IFetchDownloadFileOptions | ||
): Promise<IFetchDownloadFileResponse> { | ||
validateOptions(options); | ||
const requestUrl = getDownloadApiRequestUrl(options); | ||
const { pollInterval, progressCallback } = options; | ||
return pollDownloadApi(requestUrl, pollInterval, progressCallback); | ||
|
||
const { pollInterval, updateCache, progressCallback } = options; | ||
|
||
let cacheQueryParam: CacheQueryParam; | ||
if (updateCache) { | ||
// The download api has 2 query params that can be used for controlling cache behavior. | ||
// This one should only be used as part of an initial request to update the cache | ||
cacheQueryParam = "updateCache"; | ||
} | ||
|
||
return pollDownloadApi( | ||
requestUrl, | ||
pollInterval, | ||
cacheQueryParam, | ||
progressCallback | ||
); | ||
} | ||
|
||
function validateOptions(options: IFetchDownloadFileOptions) { | ||
|
@@ -61,7 +77,8 @@ function validateOptions(options: IFetchDownloadFileOptions) { | |
* @returns a download api url that can be polled | ||
*/ | ||
function getDownloadApiRequestUrl(options: IFetchDownloadFileOptions) { | ||
const { entity, format, context, layers, geometry, where } = options; | ||
const { entity, format, context, layers, geometry, where, updateCache } = | ||
options; | ||
|
||
const searchParams = new URLSearchParams({ | ||
redirect: "false", // Needed to get the download URL instead of the file itself | ||
|
@@ -97,44 +114,77 @@ function getDownloadApiRequestUrl(options: IFetchDownloadFileOptions) { | |
* Polls the Hub Download API until the download is ready, then returns the download file URL | ||
* | ||
* @param requestUrl Hub Download Api URL to poll | ||
* @param pollInterval interval in milliseconds to poll for job completion | ||
* @param cacheQueryParam an optional query param to control cache behavior. 'updateCache' should | ||
* only be used with the initial request when a download cache update is desired. All subsequent | ||
* requests should use'trackCacheUpdate' to follow the progress of the cache update. | ||
* for subsequent requests when polling the progress of a cache update. | ||
* @param progressCallback an optional callback to report download generation progress | ||
* @returns the final file URL | ||
*/ | ||
async function pollDownloadApi( | ||
requestUrl: string, | ||
pollInterval: number, | ||
cacheQueryParam?: CacheQueryParam, | ||
progressCallback?: downloadProgressCallback | ||
): Promise<IFetchDownloadFileResponse> { | ||
const response = await fetch(requestUrl); | ||
// If requested, append the cache query param to the request URL | ||
let withCacheQueryParam = requestUrl; | ||
if (cacheQueryParam) { | ||
withCacheQueryParam = `${requestUrl}&${cacheQueryParam}=true`; | ||
} | ||
|
||
const response = await fetch(withCacheQueryParam); | ||
|
||
if (!response.ok) { | ||
const errorBody = await response.json(); | ||
// TODO: Add standarized messageId when available | ||
throw new ArcgisHubDownloadError({ | ||
rawMessage: errorBody.message, | ||
}); | ||
} | ||
const { status, progressInPercent, resultUrl }: IHubDownloadApiResponse = | ||
await response.json(); | ||
|
||
const { | ||
status, | ||
progressInPercent, | ||
resultUrl, | ||
cacheStatus, | ||
}: IHubDownloadApiResponse = await response.json(); | ||
|
||
const operationStatus = toDownloadOperationStatus(status); | ||
if (operationStatus === DownloadOperationStatus.FAILED) { | ||
throw new HubError( | ||
"fetchHubApiDownloadFileUrl", | ||
"Download operation failed with a 200" | ||
); | ||
} | ||
progressCallback && progressCallback(operationStatus, progressInPercent); | ||
|
||
// Operation complete, return the download URL | ||
if (resultUrl) { | ||
// Operation complete, return the download URL and cache status | ||
return { | ||
type: "url", | ||
href: resultUrl, | ||
cacheStatus, | ||
}; | ||
} | ||
|
||
// Operation still in progress, poll again | ||
// Operation still in progress. Report progress if a callback was provided and poll again. | ||
progressCallback && progressCallback(operationStatus, progressInPercent); | ||
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. Moving this for the same reason as above. We don't want to emit the "complete" event from within this function. |
||
await new Promise((resolve) => setTimeout(resolve, pollInterval)); | ||
return pollDownloadApi(requestUrl, pollInterval, progressCallback); | ||
|
||
let updatedCacheQueryParam: CacheQueryParam; | ||
if (cacheQueryParam) { | ||
// After an initial request with ?updateCache=true, we need to switch to ?trackCacheUpdate=true. | ||
// This enables us to follow the progress of the cache update without causing an infinite update loop. | ||
updatedCacheQueryParam = "trackCacheUpdate"; | ||
} | ||
|
||
return pollDownloadApi( | ||
requestUrl, | ||
pollInterval, | ||
updatedCacheQueryParam, | ||
progressCallback | ||
); | ||
} | ||
|
||
/** | ||
|
@@ -223,4 +273,13 @@ interface IHubDownloadApiResponse { | |
resultUrl?: string; | ||
recordCount?: number; | ||
progressInPercent?: number; | ||
cacheStatus?: DownloadCacheStatus; | ||
} | ||
|
||
/** | ||
* @private | ||
* Query params that can be used to control cache behavior in the Hub Download API. | ||
* 'updateCache' is used with the initial request when a download cache update is desired. | ||
* 'trackCacheUpdate' is used for subsequent requests while polling the progress of a cache update. | ||
*/ | ||
type CacheQueryParam = "updateCache" | "trackCacheUpdate"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
As part of this PR, we made the decision that
fetchDownloadFile()
delegates are no longer responsible for emitting the "complete" event. It is up to the caller offetchDownloadFile()
to notify that the download is complete.