implements feature to override the retention policy on a child dataset independent of the upstream policies

This commit is contained in:
Seyi Adebajo 2018-05-17 23:46:03 -07:00
parent 30926ecb48
commit c726f136a1
20 changed files with 520 additions and 85 deletions

View File

@ -1,55 +1,205 @@
import Component from '@ember/component';
import { task } from 'ember-concurrency';
import { get, setProperties } from '@ember/object';
import { task, TaskInstance } from 'ember-concurrency';
import { get, set, computed, setProperties, getProperties } from '@ember/object';
import ComputedProperty from '@ember/object/computed';
import { inject } from '@ember/service';
import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset';
import { readDatasetByUrn } from 'wherehows-web/utils/api/datasets/dataset';
import { assert } from '@ember/debug';
import { readUpstreamDatasetsByUrn } from 'wherehows-web/utils/api/datasets/lineage';
import { IUpstreamWithComplianceMetadata } from 'wherehows-web/typings/app/datasets/lineage';
import { datasetsWithComplianceMetadata } from 'wherehows-web/constants/datasets/lineage';
import { arraySome } from 'wherehows-web/utils/array';
import { IDatasetRetention, IGetDatasetRetentionResponse } from 'wherehows-web/typings/api/datasets/retention';
import { readDatasetRetentionByUrn, saveDatasetRetentionByUrn } from 'wherehows-web/utils/api/datasets/retention';
import { readPlatforms } from 'wherehows-web/utils/api/list/platforms';
import { getSupportedPurgePolicies, PurgePolicy } from 'wherehows-web/constants';
import { IDataPlatform } from 'wherehows-web/typings/api/list/platforms';
import { action } from '@ember-decorators/object';
import { retentionObjectFactory } from 'wherehows-web/constants/datasets/retention';
import Notifications, { NotificationEvent } from 'wherehows-web/services/notifications';
/**
* Aliases the yieldable values for the container task
* @alias {IterableIterator<TaskInstance<TaskInstance<Promise<IDatasetView[]>> | Promise<IUpstreamWithComplianceMetadata[]>> | TaskInstance<Promise<IDataPlatform[]>> | TaskInstance<Promise<IGetDatasetRetentionResponse | null>>>}
*/
type ContainerYieldableResult = IterableIterator<
| TaskInstance<TaskInstance<Promise<IDatasetView[]>> | Promise<IUpstreamWithComplianceMetadata[]>>
| TaskInstance<Promise<IDataPlatform[]>>
| TaskInstance<Promise<IGetDatasetRetentionResponse | null>>
>;
export default class UpstreamDatasetContainer extends Component {
/**
* urn for the parent dataset
* References the application notifications service
* @memberof UpstreamDatasetContainer
* @type {ComputedProperty<Notifications>}
*/
notifications = <ComputedProperty<Notifications>>inject();
/**
* urn for the child dataset
* @type {string}
* @memberof UpstreamDatasetContainer
*/
upstreamUrn: string;
urn: string;
/**
* The name of the upstream dataset
* @type {IDatasetView.nativeName}
* The plaform this dataset is stored on
* @type {IDatasetView.platform}
* @memberof UpstreamDatasetContainer
*/
nativeName: IDatasetView['nativeName'];
platform: IDatasetView['platform'];
/**
* A description of the upstream dataset
* @type {IDatasetView.description}
* The list of supported purge policies for the related platform
* @type {Array<PurgePolicy>}
* @memberof UpstreamDatasetContainer
*/
description: IDatasetView['description'];
supportedPurgePolicies: Array<PurgePolicy> = [];
/**
* The list of upstream datasets for the related urn
* @type {Array<IDatasetView>}
*/
upstreamDatasets: Array<IDatasetView> = [];
/**
* List of metadata properties for upstream datasets
* @type {Array<IUpstreamWithComplianceMetadata>}
* @memberof UpstreamDatasetContainer
*/
upstreamsMetadata: Array<IUpstreamWithComplianceMetadata> = [];
/**
* Retention policy for the dataset with set urn
* @type {IDatasetRetention}
* @memberof UpstreamDatasetContainer
*/
retention: IDatasetRetention;
constructor() {
super(...arguments);
assert('A valid upstreamUrn must be provided on instantiation', typeof this.upstreamUrn === 'string');
assert(`A valid child urn must be provided on instantiation, got ${this.urn}`, !!this.urn);
assert(`A valid dataset platform must be provided on instantiation, got ${this.platform}`, !!this.platform);
this.retention = retentionObjectFactory(this.urn);
}
didUpdateAttrs() {
this._super(...arguments);
get(this, 'getUpstreamPropertiesTask').perform();
get(this, 'getContainerDataTask').perform();
}
didInsertElement() {
this._super(...arguments);
get(this, 'getUpstreamPropertiesTask').perform();
get(this, 'getContainerDataTask').perform();
}
/**
* Task to get properties for the upstream dataset
* @type {Task<Promise<IDatasetView>>, (a?: {} | undefined) => TaskInstance<Promise<IDatasetView>>>}
* Flags if any of the upstream datasets has an incomplete compliance policy
* @type ComputedProperty<boolean>
* @memberof UpstreamDatasetContainer
*/
getUpstreamPropertiesTask = task(function*(this: UpstreamDatasetContainer): IterableIterator<Promise<IDatasetView>> {
const { nativeName, description }: IDatasetView = yield readDatasetByUrn(get(this, 'upstreamUrn'));
setProperties(this, { nativeName, description });
hasIncompleteUpstream = computed('upstreamsMetadata.[]', function(this: UpstreamDatasetContainer): boolean {
const upstreamsMetadata = get(this, 'upstreamsMetadata');
const upstreamIsIncomplete = ({ hasCompliance }: IUpstreamWithComplianceMetadata): boolean => !hasCompliance;
return arraySome(upstreamIsIncomplete)(upstreamsMetadata);
});
/**
* Performs tasks related to container data instantiation
* @type {Task<ContainerYieldableResult>}
* @memberof UpstreamDatasetContainer
*/
getContainerDataTask = task(function*(this: UpstreamDatasetContainer): ContainerYieldableResult {
yield get(this, 'getUpstreamMetadataTask').perform();
yield get(this, 'getPlatformPoliciesTask').perform();
yield get(this, 'getRetentionTask').perform();
});
/**
* Task to get properties for the upstream dataset
* @type {Task<Promise<Array<IDatasetView>>>, (a?: {} | undefined) => TaskInstance<Promise<Array<IDatasetView>>>>}
* @memberof UpstreamDatasetContainer
*/
getUpstreamDatasetsTask = task(function*(
this: UpstreamDatasetContainer
): IterableIterator<Promise<Array<IDatasetView>>> {
const upstreamDatasets: Array<IDatasetView> = yield readUpstreamDatasetsByUrn(get(this, 'urn'));
return set(this, 'upstreamDatasets', upstreamDatasets);
});
/**
* Task to get and set upstream metadata for upstream datasets
* @type {Task<TaskInstance<Promise<Array<IDatasetView>>> | Promise<Array<IUpstreamWithComplianceMetadata>>>}
* @memberof UpstreamDatasetContainer
*/
getUpstreamMetadataTask = task(function*(
this: UpstreamDatasetContainer
): IterableIterator<TaskInstance<Promise<Array<IDatasetView>>> | Promise<Array<IUpstreamWithComplianceMetadata>>> {
const upstreamDatasets: Array<IDatasetView> = yield get(this, 'getUpstreamDatasetsTask').perform();
const upstreamMetadataPromises = datasetsWithComplianceMetadata(upstreamDatasets);
const upstreamsMetadata: Array<IUpstreamWithComplianceMetadata> = yield Promise.all(upstreamMetadataPromises);
set(this, 'upstreamsMetadata', upstreamsMetadata);
}).restartable();
/**
* Task to retrieve platform policies and set supported policies for the current platform
* @type {Task<Promise<Array<IDataPlatform>>, () => TaskInstance<Promise<Array<IDataPlatform>>>>}
* @memberof UpstreamDatasetContainer
*/
getPlatformPoliciesTask = task(function*(
this: UpstreamDatasetContainer
): IterableIterator<Promise<Array<IDataPlatform>>> {
const platform = get(this, 'platform');
if (platform) {
set(this, 'supportedPurgePolicies', getSupportedPurgePolicies(platform, yield readPlatforms()));
}
}).restartable();
/**
* Task to get the retention policy for the related child dataset
* @type {Task<Promise<IGetDatasetRetentionResponse | null>, () => TaskInstance<Promise<IGetDatasetRetentionResponse | null>>>}
* @memberof UpstreamDatasetContainer
*/
getRetentionTask = task(function*(
this: UpstreamDatasetContainer
): IterableIterator<Promise<IGetDatasetRetentionResponse | null>> {
const retentionResponse: IGetDatasetRetentionResponse | null = yield readDatasetRetentionByUrn(get(this, 'urn'));
const retention =
retentionResponse !== null ? retentionResponse.retentionPolicy : retentionObjectFactory(get(this, 'urn'));
setProperties(get(this, 'retention'), retention);
}).restartable();
/**
* Task to update the dataset's retention policy with the user entered changes
* @type {Task<Promise<IDatasetRetention>, () => TaskInstance<Promise<IDatasetRetention>>>}
* @memberof UpstreamDatasetContainer
*/
saveRetentionTask = task(function*(this: UpstreamDatasetContainer): IterableIterator<Promise<IDatasetRetention>> {
const {
retention,
notifications: { notify }
} = getProperties(this, ['retention', 'notifications']);
setProperties(retention, yield saveDatasetRetentionByUrn(get(this, 'urn'), retention));
notify(NotificationEvent.success, {
content: 'Successfully updated retention policy for dataset'
});
}).drop();
/**
* Handles user action to change the dataset retention policy purgeType
* @param {PurgePolicy} purgePolicy
* @memberof UpstreamDatasetContainer
*/
@action
onRetentionPolicyChange(purgePolicy: PurgePolicy) {
set(get(this, 'retention'), 'purgeType', purgePolicy);
}
}

View File

@ -1,7 +1,25 @@
import Component from '@ember/component';
import { set } from '@ember/object';
import { action } from '@ember-decorators/object';
export default class UpstreamDataset extends Component {
tagName = 'section';
classNames = ['upstream-dataset-banner'];
/**
* Flag indicating the component is in view only mode and not editable
* @type {boolean}
* @memberof UpstreamDataset
*/
isReadOnly: boolean = true;
/**
* Action handler to set component in edit mode
* @memberof UpstreamDataset
*/
@action
onEdit() {
set(this, 'isReadOnly', false);
}
}

View File

@ -7,7 +7,6 @@ import {
DatasetPlatform,
exemptPolicy,
isExempt,
missingPolicyText,
PurgePolicy,
purgePolicyProps
} from 'wherehows-web/constants';
@ -24,7 +23,7 @@ export default class PurgePolicyComponent extends Component {
* Reference to the informational text if the dataset does not have a saved purge policy
* @type {string}
*/
missingPolicyText = missingPolicyText;
missingPolicyText: string = '';
/**
* Reference to client options for each purge policy

View File

@ -82,10 +82,4 @@ const exemptPolicy = PurgePolicy.PurgeExempt;
*/
const isExempt = (policy: PurgePolicy) => policy === PurgePolicy.PurgeExempt;
/**
* User informational text for datasets without a purge policy
* @type {string}
*/
const missingPolicyText = 'This dataset does not have a current compliance purge policy.';
export { PurgePolicy, purgePolicyProps, isExempt, exemptPolicy, missingPolicyText, getSupportedPurgePolicies };
export { PurgePolicy, purgePolicyProps, isExempt, exemptPolicy, getSupportedPurgePolicies };

View File

@ -0,0 +1,32 @@
import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset';
import { IReadComplianceResult, readDatasetComplianceByUrn } from 'wherehows-web/utils/api/datasets/compliance';
import { IUpstreamWithComplianceMetadata } from 'wherehows-web/typings/app/datasets/lineage';
import { arrayMap } from 'wherehows-web/utils/array';
import { encodeUrn } from 'wherehows-web/utils/validators/urn';
/**
* Maps a dataset urn/uri, other props with a property indicating if the dataset compliance policy exists
* @param {(Pick<IDatasetView, 'uri' | 'nativeName'>)} {
* uri,
* nativeName
* }
* @returns {Promise<IUpstreamWithComplianceMetadata>}
*/
const datasetWithComplianceMetadata = async ({
uri,
nativeName
}: Pick<IDatasetView, 'uri' | 'nativeName'>): Promise<IUpstreamWithComplianceMetadata> => {
const { isNewComplianceInfo }: IReadComplianceResult = await readDatasetComplianceByUrn(encodeUrn(uri));
return { urn: uri, hasCompliance: !isNewComplianceInfo, nativeName };
};
/**
* Takes a list of datasets and maps into a list of IUpstreamWithComplianceMetadata objects
* @param {Array<IDatasetView>} datasets
* @returns {Array<Promise<IUpstreamWithComplianceMetadata>>}
*/
const datasetsWithComplianceMetadata = (
datasets: Array<IDatasetView>
): Array<Promise<IUpstreamWithComplianceMetadata>> => arrayMap(datasetWithComplianceMetadata)(datasets);
export { datasetsWithComplianceMetadata };

View File

@ -0,0 +1,14 @@
import { IDatasetRetention } from 'wherehows-web/typings/api/datasets/retention';
/**
* 'News' a IDatasetRetention instance with the provided dataset urn
* @param {string} urn
* @returns {IDatasetRetention}
*/
const retentionObjectFactory = (urn: string): IDatasetRetention => ({
datasetUrn: urn,
purgeNote: '',
purgeType: ''
});
export { retentionObjectFactory };

View File

@ -32,3 +32,4 @@
@import 'nacho/nacho-breadcrumbs';
@import 'nacho/nacho-uploader';
@import 'nacho/nacho-container';
@import 'nacho/nacho-divider';

View File

@ -5,7 +5,6 @@
&__item {
@include nacho-container;
list-style-type: none;
margin-bottom: item-spacing(4);
&--disabled {
color: set-color(grey, mid);

View File

@ -86,7 +86,8 @@
padding: 9px $item-spacing-2 11px;
margin-left: $item-spacing-5;
&:first-child {
margin-left: $item-spacing-4;
margin-left: 0;
padding-left: 0;
}
// The underline will exist on all tabs, but be transparent

View File

@ -0,0 +1,5 @@
.nacho-divider {
margin: item-spacing(5 0);
border: 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.15);
}

View File

@ -1,9 +1,4 @@
.upstream-dataset-banner {
@include nacho-container;
display: flex;
flex-direction: row;
align-items: center;
&:hover {
.upstream-dataset-banner__description {
height: item-spacing(7);
@ -28,3 +23,51 @@
}
}
}
.upstream-dataset {
@include nacho-container;
padding: item-spacing(2 5);
display: flex;
align-items: center;
&__compliance-status {
&__complete {
color: get-color(green5);
}
&__incomplete {
color: get-color(orange5);
}
}
&__upstream-link {
margin-left: item-spacing(3);
}
&__upstream-link-chevron {
margin-left: auto;
}
}
.downstream-purge-policy-header {
display: flex;
align-items: center;
margin: item-spacing(6 0 5);
}
.downstream-purge-policy {
&__no-policy {
@include nacho-container;
}
&__edit {
margin-left: auto;
display: flex;
align-items: center;
&--label {
margin-right: item-spacing(4);
font-weight: fw(normal, 2);
}
}
}

View File

@ -164,10 +164,23 @@
{{/if}}
{{#if getPlatformPoliciesTask.last.isSuccessful}}
<section class="metadata-prompt">
<header class="metadata-prompt__header">
<p>
Compliance Purge Policy
{{more-info
link="http://go/gdpr/deletions/purgePolicies"
tooltip="Click for more information Purge Policies"
}}
</p>
</header>
</section>
{{purge-policy
isEditable=(not isReadOnly)
platform=platform
missingPolicyText="This dataset does not have a current compliance purge policy."
supportedPurgePolicies=supportedPurgePolicies
purgeNote=complianceInfo.compliancePurgeNote
purgePolicy=(readonly complianceInfo.complianceType)

View File

@ -12,7 +12,10 @@
{{#if complianceInfo.fromUpstream}}
{{datasets/containers/upstream-dataset upstreamUrn=complianceInfo.datasetUrn}}
{{datasets/containers/upstream-dataset
urn=urn
platform=platform
}}
{{else}}

View File

@ -1,13 +1,17 @@
{{#if getUpstreamPropertiesTask.isRunning}}
{{#if getUpstreamMetadataTask.isRunning}}
{{pendulum-ellipsis-animation}}
{{else}}
{{datasets/upstream-dataset
nativeName=nativeName
description=description
upstreamUrn=upstreamUrn
upstreamsMetadata=upstreamsMetadata
platform=platform
retention=retention
hasIncompleteUpstream=hasIncompleteUpstream
supportedPurgePolicies=supportedPurgePolicies
saveRetentionTask=saveRetentionTask
onChange=(action "onRetentionPolicyChange")
}}
{{/if}}

View File

@ -1,32 +1,101 @@
<section class="upstream-dataset-banner__content">
<div class="upstream-dataset-banner__informational">
<i class="fa fa-info-circle fa-2x"></i>
<h3>Compliance Info</h3>
<p>
Compliance Policy on this dataset is inherited from it's parent dataset(s). To make modifications on the purge
<br>
policy on this page, please complete the Compliance Metadata on all of the parent datasets.
</p>
<p class="upstream-dataset-banner__informational__text">
Compliance Policy on this dataset is inherited from a parent dataset.
<br>
To make modifications, please update the Compliance on the parent dataset.
<br>
</p>
</div>
<hr class="nacho-divider">
<h4>Parent Dataset</h4>
{{#each upstreamsMetadata as |parent|}}
<div class="upstream-dataset">
<span class="upstream-dataset__compliance-status">
{{tooltip-on-element
text=(if parent.hasCompliance "Compliance completed for parent dataset" "Please complete the Compliance on this parent dataset")
}}
{{#if parent.hasCompliance}}
{{fa-icon "check" class="upstream-dataset__compliance-status__complete"}}
{{else}}
{{fa-icon "exclamation" class="upstream-dataset__compliance-status__incomplete"}}
{{/if}}
</span>
{{#link-to
"datasets.dataset"
"urn"
(query-params urn=parent.urn)
class="upstream-dataset__upstream-link"
}}
<strong>{{parent.nativeName}}</strong>
{{/link-to}}
{{#link-to
"datasets.dataset"
"urn"
(query-params urn=parent.urn)
class="upstream-dataset__upstream-link-chevron"
}}
<i class="glyphicon glyphicon-menu-right" aria-label="Navigate to upstream dataset"></i>
{{/link-to}}
</div>
{{/each}}
<header class="downstream-purge-policy-header">
<h4>Compliance Purge Policy</h4>
<div class="downstream-purge-policy__edit">
{{#unless hasIncompleteUpstream}}
<label for="downstream-purge-edit" class="downstream-purge-policy__edit--label">
Modifying this will not affect the purge policy on the parent dataset
</label>
{{#if isReadOnly}}
<button
class="nacho-button nacho-button--large"
id="downstream-purge-edit"
onclick={{action "onEdit"}}>
Edit
</button>
{{else}}
<button
class="nacho-button nacho-button--large"
disabled={{not retention.purgeType}}
onclick={{perform saveRetentionTask}}>
{{#if saveRetentionTask.isRunning}}
{{pendulum-ellipsis-animation}}
{{else}}
Save
{{/if}}
</button>
{{/if}}
{{/unless}}
</div>
<header class="upstream-dataset-banner__title">
Parent Dataset Name:
{{#link-to
"datasets.dataset"
"urn"
(query-params urn=upstreamUrn)
}}
<strong>{{nativeName}}</strong>
{{/link-to}}
</header>
{{#if description}}
<div class="downstream-purge-policy">
{{#if hasIncompleteUpstream}}
<p class="downstream-purge-policy__no-policy">
This dataset does not have a current compliance purge policy. To update, please complete the Compliance Metadata
on the parent dataset(s).
</p>
{{else}}
<p class="upstream-dataset-banner__description">
Parent Dataset Description:
<strong>{{description}}</strong>
</p>
{{/if}}
{{purge-policy
isEditable=(not isReadOnly)
platform=platform
supportedPurgePolicies=supportedPurgePolicies
purgeNote=retention.purgeNote
purgePolicy=(readonly retention.purgeType)
missingPolicyText="This dataset does not have a current compliance purge policy"
onPolicyChange=onChange
}}
{{/if}}
</div>
</section>

View File

@ -1,16 +1,3 @@
<section class="metadata-prompt">
<header class="metadata-prompt__header">
<p>
Compliance Purge Policy
{{more-info
link="http://go/gdpr/deletions/purgePolicies"
tooltip="Click for more information Purge Policies"
}}
</p>
</header>
</section>
<ul class="purge-policy-list">
{{#if isEditable}}
@ -51,17 +38,16 @@
{{else}}
{{#if purgePolicy}}
<li class="purge-policy-list__item">
<li class="purge-policy-list__item">
{{#if purgePolicy}}
<strong>
{{get (get purgePolicyProps purgePolicy) "displayAs"}}
</strong>
</li>
{{else}}
{{else}}
<p>{{missingPolicyText}}</p>
<sub>To update, click edit and follow the steps</sub>
{{missingPolicyText}}
{{/if}}
{{/if}}
</li>
{{/if}}
</ul>

View File

@ -0,0 +1,31 @@
import { IComplianceInfo } from 'wherehows-web/typings/api/datasets/compliance';
/**
* Describes the interface for a dataset retention policy
*
* @interface IDatasetRetention
*/
interface IDatasetRetention {
// Unused attribute for the id of the dataset, use datasetUrn instead
datasetId?: number | null;
// Urn string identifying the dataset that owns this policy
datasetUrn: string;
// Purge Policy for the dataset
purgeType: IComplianceInfo['complianceType'];
// User entered purge notation for a dataset with a purge exempt policy
purgeNote: IComplianceInfo['compliancePurgeNote'];
// Who modified this retention policy
modifiedBy?: string;
// When this policy was last modified
modifiedTime?: number;
}
/**
* Desribes the return type for requests to the endpoint returning the dataset retention policy
* @interface IGetDatasetRetentionResponse
*/
interface IGetDatasetRetentionResponse {
retentionPolicy: IDatasetRetention;
}
export { IDatasetRetention, IGetDatasetRetentionResponse };

View File

@ -0,0 +1,14 @@
import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset';
/**
* Defines the interface for the attributes used by the app to render the upstream datasets for a child
* dataset
* @interface IUpstreamWithComplianceMetadata
*/
interface IUpstreamWithComplianceMetadata {
urn: IDatasetView['uri'];
nativeName: IDatasetView['nativeName'];
hasCompliance: boolean;
}
export { IUpstreamWithComplianceMetadata };

View File

@ -0,0 +1,20 @@
import { getJSON } from 'wherehows-web/utils/api/fetcher';
import { datasetUrlByUrn } from 'wherehows-web/utils/api/datasets/shared';
import { IDatasetView } from 'wherehows-web/typings/api/datasets/dataset';
/**
* Constructs the url for a datasets upstreams
* @param {string} urn the urn for the child dataset
* @return {string}
*/
const datasetUpstreamUrlByUrn = (urn: string): string => `${datasetUrlByUrn(urn)}/upstreams`;
/**
* Fetches the list of upstream datasets for a dataset by urn
* @param {string} urn urn for the child dataset
* @return {Promise<Array<IDatasetView>>}
*/
const readUpstreamDatasetsByUrn = (urn: string): Promise<Array<IDatasetView>> =>
getJSON<Array<IDatasetView>>({ url: datasetUpstreamUrlByUrn(urn) });
export { readUpstreamDatasetsByUrn };

View File

@ -0,0 +1,39 @@
import { getJSON, postJSON } from 'wherehows-web/utils/api/fetcher';
import { datasetUrlByUrn } from 'wherehows-web/utils/api/datasets/shared';
import { IDatasetRetention, IGetDatasetRetentionResponse } from 'wherehows-web/typings/api/datasets/retention';
import { notFoundApiError } from 'wherehows-web/utils/api';
/**
* Constructs the url for a datasets retention policy
* @param {string} urn the urn for the dataset
* @return {string}
*/
const datasetRetentionUrlByUrn = (urn: string): string => `${datasetUrlByUrn(urn)}/retention`;
/**
* Fetches the list of retention policy for a dataset by urn
* @param {string} urn urn for the dataset
* @return {Promise<Array<IDatasetView>>}
*/
const readDatasetRetentionByUrn = async (urn: string): Promise<IGetDatasetRetentionResponse | null> => {
try {
return await getJSON<IGetDatasetRetentionResponse>({ url: datasetRetentionUrlByUrn(urn) });
} catch (e) {
if (notFoundApiError(e)) {
return null;
}
throw e;
}
};
/**
* Persists the dataset retention policy remotely
* @param {string} urn the urn of the dataset to save
* @param {IDatasetRetention} retention the dataset retention policy to updae
* @return {Promise<IDatasetRetention>}
*/
const saveDatasetRetentionByUrn = (urn: string, retention: IDatasetRetention): Promise<IDatasetRetention> =>
postJSON<IDatasetRetention>({ url: datasetRetentionUrlByUrn(urn), data: retention });
export { readDatasetRetentionByUrn, saveDatasetRetentionByUrn };