diff --git a/SampleService.html b/SampleService.html index f14a2026..c8d985f4 100644 --- a/SampleService.html +++ b/SampleService.html @@ -1 +1 @@ -SampleService
/*
*A KBase module: SampleService
*
*Handles creating, updating, retriving samples and linking data to samples.
*
*Note that usage of the administration flags will be logged by the service.
*/
moduleSampleService{

/*
*A boolean value, 0 for false, 1 for true.
*/
typedefintboolean;

/*
*A timestamp in epoch milliseconds.
*/
typedefinttimestamp;

/*
*A user's username.
*/
typedefstringuser;

/*
*A SampleNode ID. Must be unique within a Sample and be less than 255 characters.
*/
typedefstringnode_id;

/*
*The type of a sample node. One of:
*BioReplicate - a biological replicate. Always at the top of the sample tree.
*TechReplicate - a technical replicate.
*SubSample - a sub sample that is not a technical replicate.
*/
typedefstringsamplenode_type;

/*
*A Sample ID. Must be globally unique. Always assigned by the Sample service.
*/
typedefstringsample_id;

/*
*A link ID. Must be globally unique. Always assigned by the Sample service.
*Typically only of use to service admins.
*/
typedefstringlink_id;

/*
*A sample name. Must be less than 255 characters.
*/
typedefstringsample_name;

/*
*The version of a sample. Always > 0.
*/
typedefintversion;

/*
*A key in a metadata key/value pair. Less than 1000 unicode characters.
*/
typedefstringmetadata_key;

/*
*A key for a value associated with a piece of metadata. Less than 1000 unicode characters.
*Examples: units, value, species
*/
typedefstringmetadata_value_key;

/*
*A workspace type string.
*Specifies the workspace data type a single string in the format
*[module].[typename]:
*
*module - a string. The module name of the typespec containing the type.
*typename - a string. The name of the type as assigned by the typedef
*statement.
*
*Example: KBaseSets.SampleSet
*/
typedefstringws_type_string;

/*
*A metadata value, represented by a mapping of value keys to primitive values. An example for
*a location metadata key might be:
*{
*"name": "Castle Geyser",
*"lat": 44.463816,
*"long": -110.836471
*}
*"primitive values" means an int, float, string, or equivalent typedefs. Including any
*collection types is an error.
*/
typedefmapping<metadata_value_key,UnspecifiedObject>metadata_value;

/*
*Metadata attached to a sample.
*/
typedefmapping<metadata_key,metadata_value>metadata;

/*
*Information about a metadata key as it appeared at the data source.
*The source key and value represents the original state of the metadata before it was
*tranformed for ingestion by the sample service.
*
*key - the metadata key.
*skey - the key as it appeared at the data source.
*svalue - the value as it appeared at the data source.
*/
typedefstructure{
metadata_keykey;
metadata_keyskey;
metadata_valuesvalue;
}
SourceMetadata;

/*
*A KBase Workspace service Unique Permanent Address (UPA). E.g. 5/6/7 where 5 is the
*workspace ID, 6 the object ID, and 7 the object version.
*/
typedefstringws_upa;

/*
*An id for a unit of data within a KBase Workspace object. A single object may contain
*many data units. A dataid is expected to be unique within a single object. Must be less
*than 255 characters.
*/
typedefstringdata_id;

/*
*A node in a sample tree.
*id - the ID of the node.
*parent - the id of the parent node for the current node. BioReplicate nodes, and only
*BioReplicate nodes, do not have a parent.
*type - the type of the node.
*meta_controlled - metadata restricted by the sample controlled vocabulary and validators.
*source_meta - the pre-transformation keys and values of the controlled metadata at the
*data source for controlled metadata keys. In some cases the source metadata
*may be transformed prior to ingestion by the Sample Service; the contents of this
*data structure allows for reconstructing the original representation. The metadata
*here is not validated other than basic size checks and is provided on an
*informational basis only. The metadata keys in the SourceMetadata data structure
*must be a subset of the meta_controlled mapping keys.
*meta_user - unrestricted metadata.
*/
typedefstructure{
node_idid;
node_idparent;
samplenode_typetype;
metadatameta_controlled;
list<SourceMetadata>source_meta;
metadatameta_user;
}
SampleNode;

/*
*A Sample, consisting of a tree of subsamples and replicates.
*id - the ID of the sample.
*user - the user that saved the sample.
*node_tree - the tree(s) of sample nodes in the sample. The the roots of all trees must
*be BioReplicate nodes. All the BioReplicate nodes must be at the start of the list,
*and all child nodes must occur after their parents in the list.
*name - the name of the sample. Must be less than 255 characters.
*save_date - the date the sample version was saved.
*version - the version of the sample.
*/
typedefstructure{
sample_idid;
useruser;
list<SampleNode>node_tree;
sample_namename;
timestampsave_date;
versionversion;
}
Sample;

/*
*Access control lists for a sample. Access levels include the privileges of the lower
*access levels.
*
*owner - the user that created and owns the sample.
*admin - users that can administrate (e.g. alter ACLs) the sample.
*write - users that can write (e.g. create a new version) to the sample.
*read - users that can view the sample.
*public_read - whether any user can read the sample, regardless of permissions.
*/
typedefstructure{
userowner;
list<user>admin;
list<user>write;
list<user>read;
booleanpublic_read;
}
SampleACLs;

/*
*A Sample ID and version.
*id - the ID of the sample.
*version - the version of the sample.
*/
typedefstructure{
sample_idid;
versionversion;
}
SampleAddress;

/*
*Parameters for creating a sample.
*If Sample.id is null, a new Sample is created along with a new ID.
*Otherwise, a new version of Sample.id is created. If Sample.id does not exist, an error
*is returned.
*Any incoming user, version or timestamp in the incoming sample is ignored.
*
*sample - the sample to save.
*prior_version - if non-null, ensures that no other sample version is saved between
*prior_version and the version that is created by this save. If this is not the case,
*the sample will fail to save.
*as_admin - run the method as a service administrator. The user must have full
*administration permissions.
*as_user - create the sample as a different user. Ignored if as_admin is not true. Neither
*the administrator nor the impersonated user need have permissions to the sample if a
*new version is saved.
*/
typedefstructure{
Samplesample;
intprior_version;
booleanas_admin;
useras_user;
}
CreateSampleParams;

/*
*Create a new sample or a sample version.
*/
funcdefcreate_sample(CreateSampleParamsparams)returns(SampleAddressaddress)authenticationrequired;

/*
*get_sample parameters.
*id - the ID of the sample to retrieve.
*version - the version of the sample to retrieve, or the most recent sample if omitted.
*as_admin - get the sample regardless of ACLs as long as the user has administration read
*permissions.
*/
typedefstructure{
sample_idid;
versionversion;
booleanas_admin;
}
GetSampleParams;

/*
*Get a sample. If the version is omitted the most recent sample is returned.
*/
funcdefget_sample(GetSampleParamsparams)returns(Samplesample)authenticationoptional;

typedefstructure{
sample_idid;
versionversion;
}
SampleIdentifier;

typedefstructure{
list<SampleIdentifier>samples;
booleanas_admin;
}
GetSamplesParams;

funcdefget_samples(GetSamplesParamsparams)returns(list<Sample>samples)authenticationoptional;

/*
*get_sample_acls parameters.
*id - the ID of the sample to retrieve.
*as_admin - get the sample acls regardless of ACL contents as long as the user has
*administration read permissions.
*/
typedefstructure{
sample_idid;
booleanas_admin;
}
GetSampleACLsParams;

/*
*Get a sample's ACLs.
*/
funcdefget_sample_acls(GetSampleACLsParamsparams)returns(SampleACLsacls)authenticationoptional;

/*
*update_sample_acls parameters.
*
*id - the ID of the sample to modify.
*admin - a list of users that will receive admin privileges. Default none.
*write - a list of users that will receive write privileges. Default none.
*read - a list of users that will receive read privileges. Default none.
*remove - a list of users that will have all privileges removed. Default none.
*public_read - an integer that determines whether the sample will be set to publicly
*readable:
*> 0: public read.
*0: No change (the default).
*< 0: private.
*at_least - false, the default, indicates that the users should get the exact permissions
*as specified in the user lists, which may mean a reduction in permissions. If true,
*users that already exist in the sample ACLs will not have their permissions reduced
*as part of the ACL update unless they are in the remove list. E.g. if a user has
*write permissions and read permissions are specified in the update, no changes will
*be made to the user's permission.
*as_admin - update the sample acls regardless of sample ACL contents as long as the user has
*full service administration permissions.
*/
typedefstructure{
sample_idid;
list<user>admin;
list<user>write;
list<user>read;
list<user>remove;
intpublic_read;
booleanat_least;
booleanas_admin;
}
UpdateSampleACLsParams;

/*
*Update a sample's ACLs.
*/
funcdefupdate_sample_acls(UpdateSampleACLsParamsparams)returns()authenticationrequired;

/*
*update_samples_acls parameters.
*
*These parameters are the same as update_sample_acls, except:
*ids - a list of IDs of samples to modify.
*/
typedefstructure{
list<sample_id>ids;
list<user>admin;
list<user>write;
list<user>read;
list<user>remove;
intpublic_read;
booleanat_least;
booleanas_admin;
}
UpdateSamplesACLsParams;

/*
*Update the ACLs of many samples.
*/
funcdefupdate_samples_acls(UpdateSamplesACLsParamsparams)returns()authenticationrequired;

/*
*replace_sample_acls parameters.
*
*id - the ID of the sample to modify.
*acls - the ACLs to set on the sample.
*as_admin - replace the sample acls regardless of sample ACL contents as long as the user
*has full service administration permissions.
*/
typedefstructure{
sample_idid;
SampleACLsacls;
booleanas_admin;
}
ReplaceSampleACLsParams;

/*
*Completely overwrite a sample's ACLs. Any current ACLs are replaced by the provided
*ACLs, even if empty, and gone forever.
*
*The sample owner cannot be changed via this method.
*/
funcdefreplace_sample_acls(ReplaceSampleACLsParamsparams)returns()authenticationrequired;

/*
*get_metadata_key_static_metadata parameters.
*
*keys - the list of metadata keys to interrogate.
*prefix -
*0 (the default) to interrogate standard metadata keys.
*1 to interrogate prefix metadata keys, but require an exact match to the prefix key.
*2 to interrogate prefix metadata keys, but any keys which are a prefix of the
*provided keys will be included in the results.
*/
typedefstructure{
list<metadata_key>keys;
intprefix;
}
GetMetadataKeyStaticMetadataParams;

/*
*get_metadata_key_static_metadata results.
*
*static_metadata - the static metadata for the requested keys.
*/
typedefstructure{
metadatastatic_metadata;
}
GetMetadataKeyStaticMetadataResults;

/*
*Get static metadata for one or more metadata keys.
*
*The static metadata for a metadata key is metadata *about* the key - e.g. it may
*define the key's semantics or denote that the key is linked to an ontological ID.
*
*The static metadata does not change without the service being restarted. Client caching is
*recommended to improve performance.
*/
funcdefget_metadata_key_static_metadata(GetMetadataKeyStaticMetadataParamsparams)returns(GetMetadataKeyStaticMetadataResultsresults)authenticationnone;

/*
*create_data_link parameters.
*
*upa - the workspace UPA of the object to be linked.
*dataid - the dataid of the data to be linked, if any, within the object. If omitted the
*entire object is linked to the sample.
*id - the sample id.
*version - the sample version.
*node - the sample node.
*update - if false (the default), fail if a link already exists from the data unit (the
*combination of the UPA and dataid). if true, expire the old link and create the new
*link unless the link is already to the requested sample node, in which case the
*operation is a no-op.
*as_admin - run the method as a service administrator. The user must have full
*administration permissions.
*as_user - create the link as a different user. Ignored if as_admin is not true. Neither
*the administrator nor the impersonated user need have permissions to the data or
*sample.
*/
typedefstructure{
ws_upaupa;
data_iddataid;
sample_idid;
versionversion;
node_idnode;
booleanupdate;
booleanas_admin;
useras_user;
}
CreateDataLinkParams;

/*
*A data link from a KBase workspace object to a sample.
*
*upa - the workspace UPA of the linked object.
*dataid - the dataid of the linked data, if any, within the object. If omitted the
*entire object is linked to the sample.
*id - the sample id.
*version - the sample version.
*node - the sample node.
*createdby - the user that created the link.
*created - the time the link was created.
*expiredby - the user that expired the link, if any.
*expired - the time the link was expired, if at all.
*/
typedefstructure{
link_idlinkid;
ws_upaupa;
data_iddataid;
sample_idid;
versionversion;
node_idnode;
usercreatedby;
timestampcreated;
userexpiredby;
timestampexpired;
}
DataLink;

/*
*create_data_link results.
*
*new_link - the new link.
*/
typedefstructure{
DataLinknew_link;
}
CreateDataLinkResults;

/*
*Create a link from a KBase Workspace object to a sample.
*
*The user must have admin permissions for the sample and write permissions for the
*Workspace object.
*/
funcdefcreate_data_link(CreateDataLinkParamsparams)returns(CreateDataLinkResultsresults)authenticationrequired;

/*
*propagate_data_links parameters.
*
*id - the sample id.
*version - the sample version. (data links are propagated to)
*previous_version - the previouse sample version. (data links are propagated from)
*ignore_types - the workspace data type ignored from propagating. default empty.
*update - if false (the default), fail if a link already exists from the data unit (the
*combination of the UPA and dataid). if true, expire the old link and create the new
*link unless the link is already to the requested sample node, in which case the
*operation is a no-op.
*effective_time - the effective time at which the query should be run - the default is
*the current time. Providing a time allows for reproducibility of previous results.
*as_admin - run the method as a service administrator. The user must have full
*administration permissions.
*as_user - create the link as a different user. Ignored if as_admin is not true. Neither
*the administrator nor the impersonated user need have permissions to the data or
*sample.
*/
typedefstructure{
sample_idid;
versionversion;
versionprevious_version;
list<ws_type_string>ignore_types;
booleanupdate;
timestampeffective_time;
booleanas_admin;
useras_user;
}
PropagateDataLinkParams;

/*
*propagate_data_links results.
*
*links - the links.
*/
typedefstructure{
list<DataLink>links;
}
PropagateDataLinkResults;

/*
*Propagates data links from a previous sample to the current (latest) version
*
*The user must have admin permissions for the sample and write permissions for the
*Workspace object.
*/
funcdefpropagate_data_links(PropagateDataLinkParamsparams)returns(PropagateDataLinkResultsresults)authenticationrequired;

/*
*expire_data_link parameters.
*
*upa - the workspace upa of the object from which the link originates.
*dataid - the dataid, if any, of the data within the object from which the link originates.
*Omit for links where the link is from the entire object.
*as_admin - run the method as a service administrator. The user must have full
*administration permissions.
*as_user - expire the link as a different user. Ignored if as_admin is not true. Neither
*the administrator nor the impersonated user need have permissions to the link if a
*new version is saved.
*/
typedefstructure{
ws_upaupa;
data_iddataid;
booleanas_admin;
useras_user;
}
ExpireDataLinkParams;

/*
*Expire a link from a KBase Workspace object.
*
*The user must have admin permissions for the sample and write permissions for the
*Workspace object.
*/
funcdefexpire_data_link(ExpireDataLinkParamsparams)returns()authenticationrequired;

/*
*get_data_links_from_sample parameters.
*
*id - the sample ID.
*version - the sample version.
*effective_time - the effective time at which the query should be run - the default is
*the current time. Providing a time allows for reproducibility of previous results.
*as_admin - run the method as a service administrator. The user must have read
*administration permissions.
*/
typedefstructure{
sample_idid;
versionversion;
timestampeffective_time;
booleanas_admin;
}
GetDataLinksFromSampleParams;

/*
*get_data_links_from_sample results.
*
*links - the links.
*effective_time - the time at which the query was run. This timestamp, if saved, can be
*used when running the method again to ensure reproducible results. Note that changes
*to workspace permissions may cause results to change over time.
*/
typedefstructure{
list<DataLink>links;
timestampeffective_time;
}
GetDataLinksFromSampleResults;

/*
*Get data links to Workspace objects originating from a sample.
*
*The user must have read permissions to the sample. Only Workspace objects the user
*can read are returned.
*/
funcdefget_data_links_from_sample(GetDataLinksFromSampleParamsparams)returns(GetDataLinksFromSampleResultsresults)authenticationoptional;

/*
*get_data_links_from_sample_set parameters.
*sample_ids - a list of sample ids and versions
*effective_time - the time at which the query was run. This timestamp, if saved, can be
*used when running the method again to enqure reproducible results. Note that changes
*to workspace permissions may cause results to change over time.
*as_admin - run the method as a service administrator. The user must have read
*administration permissions.
*/
typedefstructure{
list<SampleIdentifier>sample_ids;
timestampeffective_time;
booleanas_admin;
}
GetDataLinksFromSampleSetParams;

/*
*Get all workspace object metadata linked to samples in a list of samples or sample set
*refs. Returns metadata about links to data objects. A batch version of
*get_data_links_from_sample.
*
*The user must have read permissions to the sample. A permissions error is thrown when a
*sample is found that the user has no access to.
*/
funcdefget_data_links_from_sample_set(GetDataLinksFromSampleSetParamsparams)returns(GetDataLinksFromSampleResultsresults)authenticationoptional;

/*
*get_data_links_from_data parameters.
*
*upa - the data UPA.
*effective_time - the effective time at which the query should be run - the default is
*the current time. Providing a time allows for reproducibility of previous results.
*as_admin - run the method as a service administrator. The user must have read
*administration permissions.
*/
typedefstructure{
ws_upaupa;
timestampeffective_time;
booleanas_admin;
}
GetDataLinksFromDataParams;

/*
*get_data_links_from_data results.
*
*links - the links.
*effective_time - the time at which the query was run. This timestamp, if saved, can be
*used when running the method again to ensure reproducible results.
*/
typedefstructure{
list<DataLink>links;
timestampeffective_time;
}
GetDataLinksFromDataResults;

/*
*Get data links to samples originating from Workspace data.
*
*The user must have read permissions to the workspace data.
*/
funcdefget_data_links_from_data(GetDataLinksFromDataParamsparams)returns(GetDataLinksFromDataResultsresults)authenticationoptional;

/*
*get_sample_via_data parameters.
*
*upa - the workspace UPA of the target object.
*id - the target sample id.
*version - the target sample version.
*/
typedefstructure{
ws_upaupa;
sample_idid;
versionversion;
}
GetSampleViaDataParams;

/*
*Get a sample via a workspace object. Read permissions to a workspace object grants
*read permissions to all versions of any linked samples, whether the links are expired or
*not. This method allows for fetching samples when the user does not have explicit
*read access to the sample.
*/
funcdefget_sample_via_data(GetSampleViaDataParamsparams)returns(Samplesample)authenticationoptional;

/*
*get_data_link parameters.
*
*linkid - the link ID.
*/
typedefstructure{
link_idlinkid;
}
GetDataLinkParams;

/*
*Get a link, expired or not, by its ID. This method requires read administration privileges
*for the service.
*/
funcdefget_data_link(GetDataLinkParamsparams)returns(DataLinklink)authenticationrequired;

/*
*Provide sample and run through the validation steps, but without saving them. Allows all the samples to be evaluated for validity first so potential errors can be addressed.
*/
typedefstructure{
list<Sample>samples;
}
ValidateSamplesParams;

typedefstructure{
stringmessage;
stringdev_message;
sample_namesample_name;
node_idnode;
metadata_keykey;
stringsubkey;
}
ValidateSamplesError;

typedefstructure{
list<ValidateSamplesError>errors;
}
ValidateSamplesResults;

funcdefvalidate_samples(ValidateSamplesParamsparams)returns(ValidateSamplesResultsresults)authenticationrequired;
};

Function Index

create_data_link
create_sample
expire_data_link
get_data_link
get_data_links_from_data
get_data_links_from_sample
get_data_links_from_sample_set
get_metadata_key_static_metadata
get_sample
get_sample_acls
get_sample_via_data
get_samples
propagate_data_links
replace_sample_acls
update_sample_acls
update_samples_acls
validate_samples

Type Index

boolean
CreateDataLinkParams
CreateDataLinkResults
CreateSampleParams
data_id
DataLink
ExpireDataLinkParams
GetDataLinkParams
GetDataLinksFromDataParams
GetDataLinksFromDataResults
GetDataLinksFromSampleParams
GetDataLinksFromSampleResults
GetDataLinksFromSampleSetParams
GetMetadataKeyStaticMetadataParams
GetMetadataKeyStaticMetadataResults
GetSampleACLsParams
GetSampleParams
GetSamplesParams
GetSampleViaDataParams
link_id
metadata
metadata_key
metadata_value
metadata_value_key
node_id
PropagateDataLinkParams
PropagateDataLinkResults
ReplaceSampleACLsParams
Sample
sample_id
sample_name
SampleACLs
SampleAddress
SampleIdentifier
SampleNode
samplenode_type
SourceMetadata
timestamp
UpdateSampleACLsParams
UpdateSamplesACLsParams
user
ValidateSamplesError
ValidateSamplesParams
ValidateSamplesResults
version
ws_type_string
ws_upa
\ No newline at end of file +SampleService
/*
*A KBase module: SampleService
*
*Handles creating, updating, retriving samples and linking data to samples.
*
*Note that usage of the administration flags will be logged by the service.
*/
moduleSampleService{

/*
*A boolean value, 0 for false, 1 for true.
*/
typedefintboolean;

/*
*A timestamp in epoch milliseconds.
*/
typedefinttimestamp;

/*
*A user's username.
*/
typedefstringuser;

/*
*A SampleNode ID. Must be unique within a Sample and be less than 255 characters.
*/
typedefstringnode_id;

/*
*The type of a sample node. One of:
*BioReplicate - a biological replicate. Always at the top of the sample tree.
*TechReplicate - a technical replicate.
*SubSample - a sub sample that is not a technical replicate.
*/
typedefstringsamplenode_type;

/*
*A Sample ID. Must be globally unique. Always assigned by the Sample service.
*/
typedefstringsample_id;

/*
*A link ID. Must be globally unique. Always assigned by the Sample service.
*Typically only of use to service admins.
*/
typedefstringlink_id;

/*
*A sample name. Must be less than 255 characters.
*/
typedefstringsample_name;

/*
*The version of a sample. Always > 0.
*/
typedefintversion;

/*
*A key in a metadata key/value pair. Less than 1000 unicode characters.
*/
typedefstringmetadata_key;

/*
*A key for a value associated with a piece of metadata. Less than 1000 unicode characters.
*Examples: units, value, species
*/
typedefstringmetadata_value_key;

/*
*A workspace type string.
*Specifies the workspace data type a single string in the format
*[module].[typename]:
*
*module - a string. The module name of the typespec containing the type.
*typename - a string. The name of the type as assigned by the typedef
*statement.
*
*Example: KBaseSets.SampleSet
*/
typedefstringws_type_string;

/*
*A metadata value, represented by a mapping of value keys to primitive values. An example for
*a location metadata key might be:
*{
*"name": "Castle Geyser",
*"lat": 44.463816,
*"long": -110.836471
*}
*"primitive values" means an int, float, string, or equivalent typedefs. Including any
*collection types is an error.
*/
typedefmapping<metadata_value_key,UnspecifiedObject>metadata_value;

/*
*Metadata attached to a sample.
*/
typedefmapping<metadata_key,metadata_value>metadata;

/*
*Information about a metadata key as it appeared at the data source.
*The source key and value represents the original state of the metadata before it was
*tranformed for ingestion by the sample service.
*
*key - the metadata key.
*skey - the key as it appeared at the data source.
*svalue - the value as it appeared at the data source.
*/
typedefstructure{
metadata_keykey;
metadata_keyskey;
metadata_valuesvalue;
}
SourceMetadata;

/*
*A KBase Workspace service Unique Permanent Address (UPA). E.g. 5/6/7 where 5 is the
*workspace ID, 6 the object ID, and 7 the object version.
*/
typedefstringws_upa;

/*
*An id for a unit of data within a KBase Workspace object. A single object may contain
*many data units. A dataid is expected to be unique within a single object. Must be less
*than 255 characters.
*/
typedefstringdata_id;

/*
*A node in a sample tree.
*id - the ID of the node.
*parent - the id of the parent node for the current node. BioReplicate nodes, and only
*BioReplicate nodes, do not have a parent.
*type - the type of the node.
*meta_controlled - metadata restricted by the sample controlled vocabulary and validators.
*source_meta - the pre-transformation keys and values of the controlled metadata at the
*data source for controlled metadata keys. In some cases the source metadata
*may be transformed prior to ingestion by the Sample Service; the contents of this
*data structure allows for reconstructing the original representation. The metadata
*here is not validated other than basic size checks and is provided on an
*informational basis only. The metadata keys in the SourceMetadata data structure
*must be a subset of the meta_controlled mapping keys.
*meta_user - unrestricted metadata.
*/
typedefstructure{
node_idid;
node_idparent;
samplenode_typetype;
metadatameta_controlled;
list<SourceMetadata>source_meta;
metadatameta_user;
}
SampleNode;

/*
*A Sample, consisting of a tree of subsamples and replicates.
*id - the ID of the sample.
*user - the user that saved the sample.
*node_tree - the tree(s) of sample nodes in the sample. The the roots of all trees must
*be BioReplicate nodes. All the BioReplicate nodes must be at the start of the list,
*and all child nodes must occur after their parents in the list.
*name - the name of the sample. Must be less than 255 characters.
*save_date - the date the sample version was saved.
*version - the version of the sample.
*/
typedefstructure{
sample_idid;
useruser;
list<SampleNode>node_tree;
sample_namename;
timestampsave_date;
versionversion;
}
Sample;

/*
*Access control lists for a sample. Access levels include the privileges of the lower
*access levels.
*
*owner - the user that created and owns the sample.
*admin - users that can administrate (e.g. alter ACLs) the sample.
*write - users that can write (e.g. create a new version) to the sample.
*read - users that can view the sample.
*public_read - whether any user can read the sample, regardless of permissions.
*/
typedefstructure{
userowner;
list<user>admin;
list<user>write;
list<user>read;
booleanpublic_read;
}
SampleACLs;

/*
*A Sample ID and version.
*id - the ID of the sample.
*version - the version of the sample.
*/
typedefstructure{
sample_idid;
versionversion;
}
SampleAddress;

/*
*Parameters for creating a sample.
*If Sample.id is null, a new Sample is created along with a new ID.
*Otherwise, a new version of Sample.id is created. If Sample.id does not exist, an error
*is returned.
*Any incoming user, version or timestamp in the incoming sample is ignored.
*
*sample - the sample to save.
*prior_version - if non-null, ensures that no other sample version is saved between
*prior_version and the version that is created by this save. If this is not the case,
*the sample will fail to save.
*as_admin - run the method as a service administrator. The user must have full
*administration permissions.
*as_user - create the sample as a different user. Ignored if as_admin is not true. Neither
*the administrator nor the impersonated user need have permissions to the sample if a
*new version is saved.
*/
typedefstructure{
Samplesample;
intprior_version;
booleanas_admin;
useras_user;
}
CreateSampleParams;

/*
*Create a new sample or a sample version.
*/
funcdefcreate_sample(CreateSampleParamsparams)returns(SampleAddressaddress)authenticationrequired;

/*
*get_sample parameters.
*id - the ID of the sample to retrieve.
*version - the version of the sample to retrieve, or the most recent sample if omitted.
*as_admin - get the sample regardless of ACLs as long as the user has administration read
*permissions.
*/
typedefstructure{
sample_idid;
versionversion;
booleanas_admin;
}
GetSampleParams;

/*
*Get a sample. If the version is omitted the most recent sample is returned.
*/
funcdefget_sample(GetSampleParamsparams)returns(Samplesample)authenticationoptional;

typedefstructure{
sample_idid;
versionversion;
}
SampleIdentifier;

typedefstructure{
list<SampleIdentifier>samples;
booleanas_admin;
}
GetSamplesParams;

funcdefget_samples(GetSamplesParamsparams)returns(list<Sample>samples)authenticationoptional;

/*
*get_sample_acls parameters.
*id - the ID of the sample to retrieve.
*as_admin - get the sample acls regardless of ACL contents as long as the user has
*administration read permissions.
*/
typedefstructure{
sample_idid;
booleanas_admin;
}
GetSampleACLsParams;

/*
*Get a sample's ACLs.
*/
funcdefget_sample_acls(GetSampleACLsParamsparams)returns(SampleACLsacls)authenticationoptional;

/*
*update_sample_acls parameters.
*
*id - the ID of the sample to modify.
*admin - a list of users that will receive admin privileges. Default none.
*write - a list of users that will receive write privileges. Default none.
*read - a list of users that will receive read privileges. Default none.
*remove - a list of users that will have all privileges removed. Default none.
*public_read - an integer that determines whether the sample will be set to publicly
*readable:
*> 0: public read.
*0: No change (the default).
*< 0: private.
*at_least - false, the default, indicates that the users should get the exact permissions
*as specified in the user lists, which may mean a reduction in permissions. If true,
*users that already exist in the sample ACLs will not have their permissions reduced
*as part of the ACL update unless they are in the remove list. E.g. if a user has
*write permissions and read permissions are specified in the update, no changes will
*be made to the user's permission.
*as_admin - update the sample acls regardless of sample ACL contents as long as the user has
*full service administration permissions.
*/
typedefstructure{
sample_idid;
list<user>admin;
list<user>write;
list<user>read;
list<user>remove;
intpublic_read;
booleanat_least;
booleanas_admin;
}
UpdateSampleACLsParams;

/*
*Update a sample's ACLs.
*/
funcdefupdate_sample_acls(UpdateSampleACLsParamsparams)returns()authenticationrequired;

/*
*update_samples_acls parameters.
*
*These parameters are the same as update_sample_acls, except:
*ids - a list of IDs of samples to modify.
*/
typedefstructure{
list<sample_id>ids;
list<user>admin;
list<user>write;
list<user>read;
list<user>remove;
intpublic_read;
booleanat_least;
booleanas_admin;
}
UpdateSamplesACLsParams;

/*
*Update the ACLs of many samples.
*/
funcdefupdate_samples_acls(UpdateSamplesACLsParamsparams)returns()authenticationrequired;

/*
*replace_sample_acls parameters.
*
*id - the ID of the sample to modify.
*acls - the ACLs to set on the sample.
*as_admin - replace the sample acls regardless of sample ACL contents as long as the user
*has full service administration permissions.
*/
typedefstructure{
sample_idid;
SampleACLsacls;
booleanas_admin;
}
ReplaceSampleACLsParams;

/*
*Completely overwrite a sample's ACLs. Any current ACLs are replaced by the provided
*ACLs, even if empty, and gone forever.
*
*The sample owner cannot be changed via this method.
*/
funcdefreplace_sample_acls(ReplaceSampleACLsParamsparams)returns()authenticationrequired;

/*
*get_metadata_key_static_metadata parameters.
*
*keys - the list of metadata keys to interrogate.
*prefix -
*0 (the default) to interrogate standard metadata keys.
*1 to interrogate prefix metadata keys, but require an exact match to the prefix key.
*2 to interrogate prefix metadata keys, but any keys which are a prefix of the
*provided keys will be included in the results.
*/
typedefstructure{
list<metadata_key>keys;
intprefix;
}
GetMetadataKeyStaticMetadataParams;

/*
*get_metadata_key_static_metadata results.
*
*static_metadata - the static metadata for the requested keys.
*/
typedefstructure{
metadatastatic_metadata;
}
GetMetadataKeyStaticMetadataResults;

/*
*Get static metadata for one or more metadata keys.
*
*The static metadata for a metadata key is metadata *about* the key - e.g. it may
*define the key's semantics or denote that the key is linked to an ontological ID.
*
*The static metadata does not change without the service being restarted. Client caching is
*recommended to improve performance.
*/
funcdefget_metadata_key_static_metadata(GetMetadataKeyStaticMetadataParamsparams)returns(GetMetadataKeyStaticMetadataResultsresults)authenticationnone;

/*
*create_data_link parameters.
*
*upa - the workspace UPA of the object to be linked.
*dataid - the dataid of the data to be linked, if any, within the object. If omitted the
*entire object is linked to the sample.
*id - the sample id.
*version - the sample version.
*node - the sample node.
*update - if false (the default), fail if a link already exists from the data unit (the
*combination of the UPA and dataid). if true, expire the old link and create the new
*link unless the link is already to the requested sample node, in which case the
*operation is a no-op.
*as_admin - run the method as a service administrator. The user must have full
*administration permissions.
*as_user - create the link as a different user. Ignored if as_admin is not true. Neither
*the administrator nor the impersonated user need have permissions to the data or
*sample.
*/
typedefstructure{
ws_upaupa;
data_iddataid;
sample_idid;
versionversion;
node_idnode;
booleanupdate;
booleanas_admin;
useras_user;
}
CreateDataLinkParams;

/*
*A data link from a KBase workspace object to a sample.
*
*upa - the workspace UPA of the linked object.
*dataid - the dataid of the linked data, if any, within the object. If omitted the
*entire object is linked to the sample.
*id - the sample id.
*version - the sample version.
*node - the sample node.
*createdby - the user that created the link.
*created - the time the link was created.
*expiredby - the user that expired the link, if any.
*expired - the time the link was expired, if at all.
*/
typedefstructure{
link_idlinkid;
ws_upaupa;
data_iddataid;
sample_idid;
versionversion;
node_idnode;
usercreatedby;
timestampcreated;
userexpiredby;
timestampexpired;
}
DataLink;

/*
*create_data_link results.
*
*new_link - the new link.
*/
typedefstructure{
DataLinknew_link;
}
CreateDataLinkResults;

/*
*Create a link from a KBase Workspace object to a sample.
*
*The user must have admin permissions for the sample and write permissions for the
*Workspace object.
*/
funcdefcreate_data_link(CreateDataLinkParamsparams)returns(CreateDataLinkResultsresults)authenticationrequired;

/*
*propagate_data_links parameters.
*
*id - the sample id.
*version - the sample version. (data links are propagated to)
*previous_version - the previouse sample version. (data links are propagated from)
*ignore_types - the workspace data type ignored from propagating. default empty.
*update - if false (the default), fail if a link already exists from the data unit (the
*combination of the UPA and dataid). if true, expire the old link and create the new
*link unless the link is already to the requested sample node, in which case the
*operation is a no-op.
*effective_time - the effective time at which the query should be run - the default is
*the current time. Providing a time allows for reproducibility of previous results.
*as_admin - run the method as a service administrator. The user must have full
*administration permissions.
*as_user - create the link as a different user. Ignored if as_admin is not true. Neither
*the administrator nor the impersonated user need have permissions to the data or
*sample.
*/
typedefstructure{
sample_idid;
versionversion;
versionprevious_version;
list<ws_type_string>ignore_types;
booleanupdate;
timestampeffective_time;
booleanas_admin;
useras_user;
}
PropagateDataLinkParams;

/*
*propagate_data_links results.
*
*links - the links.
*/
typedefstructure{
list<DataLink>links;
}
PropagateDataLinkResults;

/*
*Propagates data links from a previous sample to the current (latest) version
*
*The user must have admin permissions for the sample and write permissions for the
*Workspace object.
*/
funcdefpropagate_data_links(PropagateDataLinkParamsparams)returns(PropagateDataLinkResultsresults)authenticationrequired;

/*
*expire_data_link parameters.
*
*upa - the workspace upa of the object from which the link originates.
*dataid - the dataid, if any, of the data within the object from which the link originates.
*Omit for links where the link is from the entire object.
*as_admin - run the method as a service administrator. The user must have full
*administration permissions.
*as_user - expire the link as a different user. Ignored if as_admin is not true. Neither
*the administrator nor the impersonated user need have permissions to the link if a
*new version is saved.
*/
typedefstructure{
ws_upaupa;
data_iddataid;
booleanas_admin;
useras_user;
}
ExpireDataLinkParams;

/*
*Expire a link from a KBase Workspace object.
*
*The user must have admin permissions for the sample and write permissions for the
*Workspace object.
*/
funcdefexpire_data_link(ExpireDataLinkParamsparams)returns()authenticationrequired;

/*
*label_data_links parameters.
*
*links - the the links to be labeled.
*add_labels - the labels to be added to the links.
*remove_labels - the labels to be removed from the links.
*as_admin - run the method as a service administrator. The user must have full
*administration permissions.
*as_user - label the links as a different user. Ignored if as_admin is not true. Neither
*the administrator nor the impersonated user need have permissions to the link if a
*new version is saved.
*/
typedefstructure{
list<DataLink>links;
list<string>add_labels;
list<string>remove_labels;
booleanas_admin;
useras_user;
}
LabelDataLinksParams;

/*
*Label data links.
*
*The user must have write permissions for the Workspace object.
*/
funcdeflabel_data_links(LabelDataLinksParamsparams)returns()authenticationrequired;

/*
*get_data_links_from_sample parameters.
*
*id - the sample ID.
*version - the sample version.
*effective_time - the effective time at which the query should be run - the default is
*the current time. Providing a time allows for reproducibility of previous results.
*as_admin - run the method as a service administrator. The user must have read
*administration permissions.
*/
typedefstructure{
sample_idid;
versionversion;
timestampeffective_time;
booleanas_admin;
}
GetDataLinksFromSampleParams;

/*
*get_data_links_from_sample results.
*
*links - the links.
*effective_time - the time at which the query was run. This timestamp, if saved, can be
*used when running the method again to ensure reproducible results. Note that changes
*to workspace permissions may cause results to change over time.
*/
typedefstructure{
list<DataLink>links;
timestampeffective_time;
}
GetDataLinksFromSampleResults;

/*
*Get data links to Workspace objects originating from a sample.
*
*The user must have read permissions to the sample. Only Workspace objects the user
*can read are returned.
*/
funcdefget_data_links_from_sample(GetDataLinksFromSampleParamsparams)returns(GetDataLinksFromSampleResultsresults)authenticationoptional;

/*
*get_data_links_from_sample_set parameters.
*sample_ids - a list of sample ids and versions
*effective_time - the time at which the query was run. This timestamp, if saved, can be
*used when running the method again to enqure reproducible results. Note that changes
*to workspace permissions may cause results to change over time.
*as_admin - run the method as a service administrator. The user must have read
*administration permissions.
*/
typedefstructure{
list<SampleIdentifier>sample_ids;
timestampeffective_time;
booleanas_admin;
}
GetDataLinksFromSampleSetParams;

/*
*Get all workspace object metadata linked to samples in a list of samples or sample set
*refs. Returns metadata about links to data objects. A batch version of
*get_data_links_from_sample.
*
*The user must have read permissions to the sample. A permissions error is thrown when a
*sample is found that the user has no access to.
*/
funcdefget_data_links_from_sample_set(GetDataLinksFromSampleSetParamsparams)returns(GetDataLinksFromSampleResultsresults)authenticationoptional;

/*
*get_data_links_from_data parameters.
*
*upa - the data UPA.
*effective_time - the effective time at which the query should be run - the default is
*the current time. Providing a time allows for reproducibility of previous results.
*as_admin - run the method as a service administrator. The user must have read
*administration permissions.
*/
typedefstructure{
ws_upaupa;
timestampeffective_time;
booleanas_admin;
}
GetDataLinksFromDataParams;

/*
*get_data_links_from_data results.
*
*links - the links.
*effective_time - the time at which the query was run. This timestamp, if saved, can be
*used when running the method again to ensure reproducible results.
*/
typedefstructure{
list<DataLink>links;
timestampeffective_time;
}
GetDataLinksFromDataResults;

/*
*Get data links to samples originating from Workspace data.
*
*The user must have read permissions to the workspace data.
*/
funcdefget_data_links_from_data(GetDataLinksFromDataParamsparams)returns(GetDataLinksFromDataResultsresults)authenticationoptional;

/*
*get_sample_via_data parameters.
*
*upa - the workspace UPA of the target object.
*id - the target sample id.
*version - the target sample version.
*/
typedefstructure{
ws_upaupa;
sample_idid;
versionversion;
}
GetSampleViaDataParams;

/*
*Get a sample via a workspace object. Read permissions to a workspace object grants
*read permissions to all versions of any linked samples, whether the links are expired or
*not. This method allows for fetching samples when the user does not have explicit
*read access to the sample.
*/
funcdefget_sample_via_data(GetSampleViaDataParamsparams)returns(Samplesample)authenticationoptional;

/*
*get_data_link parameters.
*
*linkid - the link ID.
*/
typedefstructure{
link_idlinkid;
}
GetDataLinkParams;

/*
*Get a link, expired or not, by its ID. This method requires read administration privileges
*for the service.
*/
funcdefget_data_link(GetDataLinkParamsparams)returns(DataLinklink)authenticationrequired;

/*
*Provide sample and run through the validation steps, but without saving them. Allows all the samples to be evaluated for validity first so potential errors can be addressed.
*/
typedefstructure{
list<Sample>samples;
}
ValidateSamplesParams;

typedefstructure{
stringmessage;
stringdev_message;
sample_namesample_name;
node_idnode;
metadata_keykey;
stringsubkey;
}
ValidateSamplesError;

typedefstructure{
list<ValidateSamplesError>errors;
}
ValidateSamplesResults;

funcdefvalidate_samples(ValidateSamplesParamsparams)returns(ValidateSamplesResultsresults)authenticationrequired;
};

Function Index

create_data_link
create_sample
expire_data_link
get_data_link
get_data_links_from_data
get_data_links_from_sample
get_data_links_from_sample_set
get_metadata_key_static_metadata
get_sample
get_sample_acls
get_sample_via_data
get_samples
label_data_links
propagate_data_links
replace_sample_acls
update_sample_acls
update_samples_acls
validate_samples

Type Index

boolean
CreateDataLinkParams
CreateDataLinkResults
CreateSampleParams
data_id
DataLink
ExpireDataLinkParams
GetDataLinkParams
GetDataLinksFromDataParams
GetDataLinksFromDataResults
GetDataLinksFromSampleParams
GetDataLinksFromSampleResults
GetDataLinksFromSampleSetParams
GetMetadataKeyStaticMetadataParams
GetMetadataKeyStaticMetadataResults
GetSampleACLsParams
GetSampleParams
GetSamplesParams
GetSampleViaDataParams
LabelDataLinksParams
link_id
metadata
metadata_key
metadata_value
metadata_value_key
node_id
PropagateDataLinkParams
PropagateDataLinkResults
ReplaceSampleACLsParams
Sample
sample_id
sample_name
SampleACLs
SampleAddress
SampleIdentifier
SampleNode
samplenode_type
SourceMetadata
timestamp
UpdateSampleACLsParams
UpdateSamplesACLsParams
user
ValidateSamplesError
ValidateSamplesParams
ValidateSamplesResults
version
ws_type_string
ws_upa
\ No newline at end of file diff --git a/SampleService.spec b/SampleService.spec index 3e5a28be..cf1d18ab 100644 --- a/SampleService.spec +++ b/SampleService.spec @@ -372,6 +372,7 @@ module SampleService { node_id node; boolean update; boolean as_admin; + list labels; user as_user; } CreateDataLinkParams; @@ -487,6 +488,31 @@ module SampleService { */ funcdef expire_data_link(ExpireDataLinkParams params) returns() authentication required; + /* label_data_links parameters. + + links - the the links to be labeled. + add_labels - the labels to be added to the links. + remove_labels - the labels to be removed from the links. + as_admin - run the method as a service administrator. The user must have full + administration permissions. + as_user - label the links as a different user. Ignored if as_admin is not true. Neither + the administrator nor the impersonated user need have permissions to the link if a + new version is saved. + */ + typedef structure { + list links; + list add_labels; + list remove_labels; + boolean as_admin; + user as_user; + } LabelDataLinksParams; + + /* Label data links. + + The user must have write permissions for the Workspace object. + */ + funcdef label_data_links(LabelDataLinksParams params) returns() authentication required; + /* get_data_links_from_sample parameters. id - the sample ID. diff --git a/lib/SampleService/SampleServiceClient.py b/lib/SampleService/SampleServiceClient.py index ecc37820..71d9ce79 100644 --- a/lib/SampleService/SampleServiceClient.py +++ b/lib/SampleService/SampleServiceClient.py @@ -646,6 +646,53 @@ def expire_data_link(self, params, context=None): return self._client.call_method('SampleService.expire_data_link', [params], self._service_ver, context) + def label_data_links(self, params, context=None): + """ + Label data links. + The user must have write permissions for the Workspace object. + :param params: instance of type "LabelDataLinksParams" + (label_data_links parameters. links - the the links to be labeled. + add_labels - the labels to be added to the links. remove_labels - + the labels to be removed from the links. as_admin - run the method + as a service administrator. The user must have full administration + permissions. as_user - label the links as a different user. + Ignored if as_admin is not true. Neither the administrator nor the + impersonated user need have permissions to the link if a new + version is saved.) -> structure: parameter "links" of list of type + "DataLink" (A data link from a KBase workspace object to a sample. + upa - the workspace UPA of the linked object. dataid - the dataid + of the linked data, if any, within the object. If omitted the + entire object is linked to the sample. id - the sample id. version + - the sample version. node - the sample node. createdby - the user + that created the link. created - the time the link was created. + expiredby - the user that expired the link, if any. expired - the + time the link was expired, if at all.) -> structure: parameter + "linkid" of type "link_id" (A link ID. Must be globally unique. + Always assigned by the Sample service. Typically only of use to + service admins.), parameter "upa" of type "ws_upa" (A KBase + Workspace service Unique Permanent Address (UPA). E.g. 5/6/7 where + 5 is the workspace ID, 6 the object ID, and 7 the object + version.), parameter "dataid" of type "data_id" (An id for a unit + of data within a KBase Workspace object. A single object may + contain many data units. A dataid is expected to be unique within + a single object. Must be less than 255 characters.), parameter + "id" of type "sample_id" (A Sample ID. Must be globally unique. + Always assigned by the Sample service.), parameter "version" of + type "version" (The version of a sample. Always > 0.), parameter + "node" of type "node_id" (A SampleNode ID. Must be unique within a + Sample and be less than 255 characters.), parameter "createdby" of + type "user" (A user's username.), parameter "created" of type + "timestamp" (A timestamp in epoch milliseconds.), parameter + "expiredby" of type "user" (A user's username.), parameter + "expired" of type "timestamp" (A timestamp in epoch + milliseconds.), parameter "add_labels" of list of String, + parameter "remove_labels" of list of String, parameter "as_admin" + of type "boolean" (A boolean value, 0 for false, 1 for true.), + parameter "as_user" of type "user" (A user's username.) + """ + return self._client.call_method('SampleService.label_data_links', + [params], self._service_ver, context) + def get_data_links_from_sample(self, params, context=None): """ Get data links to Workspace objects originating from a sample. diff --git a/lib/SampleService/SampleServiceImpl.py b/lib/SampleService/SampleServiceImpl.py index c27c9c54..3627115a 100644 --- a/lib/SampleService/SampleServiceImpl.py +++ b/lib/SampleService/SampleServiceImpl.py @@ -32,6 +32,7 @@ from SampleService.impl_methods import ( update_samples_acls as _update_samples_acls ) +from SampleService.core.workspace import DataUnitID _CTX_USER = 'user_id' _CTX_TOKEN = 'token' @@ -708,7 +709,7 @@ def create_data_link(self, ctx, params): # ctx is the context object # return variables are: results #BEGIN create_data_link - duid, sna, update = _create_data_link_params(params) + duid, sna, update, labels = _create_data_link_params(params) as_admin, user = _get_admin_request_from_object(params, 'as_admin', 'as_user') _check_admin( self._user_lookup, ctx[_CTX_TOKEN], _AdminPermission.FULL, @@ -887,6 +888,69 @@ def expire_data_link(self, ctx, params): #END expire_data_link pass + def label_data_links(self, ctx, params): + """ + Label data links. + The user must have write permissions for the Workspace object. + :param params: instance of type "LabelDataLinksParams" + (label_data_links parameters. links - the the links to be labeled. + add_labels - the labels to be added to the links. remove_labels - + the labels to be removed from the links. as_admin - run the method + as a service administrator. The user must have full administration + permissions. as_user - label the links as a different user. + Ignored if as_admin is not true. Neither the administrator nor the + impersonated user need have permissions to the link if a new + version is saved.) -> structure: parameter "links" of list of type + "DataLink" (A data link from a KBase workspace object to a sample. + upa - the workspace UPA of the linked object. dataid - the dataid + of the linked data, if any, within the object. If omitted the + entire object is linked to the sample. id - the sample id. version + - the sample version. node - the sample node. createdby - the user + that created the link. created - the time the link was created. + expiredby - the user that expired the link, if any. expired - the + time the link was expired, if at all.) -> structure: parameter + "linkid" of type "link_id" (A link ID. Must be globally unique. + Always assigned by the Sample service. Typically only of use to + service admins.), parameter "upa" of type "ws_upa" (A KBase + Workspace service Unique Permanent Address (UPA). E.g. 5/6/7 where + 5 is the workspace ID, 6 the object ID, and 7 the object + version.), parameter "dataid" of type "data_id" (An id for a unit + of data within a KBase Workspace object. A single object may + contain many data units. A dataid is expected to be unique within + a single object. Must be less than 255 characters.), parameter + "id" of type "sample_id" (A Sample ID. Must be globally unique. + Always assigned by the Sample service.), parameter "version" of + type "version" (The version of a sample. Always > 0.), parameter + "node" of type "node_id" (A SampleNode ID. Must be unique within a + Sample and be less than 255 characters.), parameter "createdby" of + type "user" (A user's username.), parameter "created" of type + "timestamp" (A timestamp in epoch milliseconds.), parameter + "expiredby" of type "user" (A user's username.), parameter + "expired" of type "timestamp" (A timestamp in epoch + milliseconds.), parameter "add_labels" of list of String, + parameter "remove_labels" of list of String, parameter "as_admin" + of type "boolean" (A boolean value, 0 for false, 1 for true.), + parameter "as_user" of type "user" (A user's username.) + """ + # ctx is the context object + #BEGIN label_data_links + as_admin, user = _get_admin_request_from_object(params, 'as_admin', 'as_user') + _check_admin( + self._user_lookup, ctx[_CTX_TOKEN], _AdminPermission.FULL, + # pretty annoying to test ctx.log_info is working, do it manually + 'label_data_links', ctx.log_info, as_user=user, skip_check=not as_admin) + + duids = [DataUnitID(dl.get('ws_upa'), dl.get('data_id')) for dl in params.get('links')] + + self._samples.label_data_links( + user if user else _UserID(ctx[_CTX_USER]), + duids, + params.get('add_labels',[]), + params.get('remove_labels',[]), + ) + #END label_data_links + pass + def get_data_links_from_sample(self, ctx, params): """ Get data links to Workspace objects originating from a sample. diff --git a/lib/SampleService/core/api_translation.py b/lib/SampleService/core/api_translation.py index 14b73fde..ebc02e4a 100644 --- a/lib/SampleService/core/api_translation.py +++ b/lib/SampleService/core/api_translation.py @@ -524,6 +524,7 @@ def create_data_link_params(params: Dict[str, Any]) -> Tuple[DataUnitID, SampleN upa - workspace object UPA dataid - ID of the data within the workspace object update - whether the link should be updated + labels - list of labels to add to the link :param params: the parameters. :returns: a tuple consisting of: @@ -541,6 +542,7 @@ def create_data_link_params(params: Dict[str, Any]) -> Tuple[DataUnitID, SampleN _cast(str, _check_string_int(params, 'node', True)) ) duid = get_data_unit_id_from_object(params) + labels = params.get('labels', []) return (duid, sna, bool(params.get('update'))) diff --git a/lib/SampleService/core/data_link.py b/lib/SampleService/core/data_link.py index a8895c71..ab20afcb 100644 --- a/lib/SampleService/core/data_link.py +++ b/lib/SampleService/core/data_link.py @@ -4,6 +4,7 @@ ''' from __future__ import annotations +from typing import Optional, List import datetime import uuid @@ -14,6 +15,7 @@ from SampleService.core.user import UserID from SampleService.core.workspace import DataUnitID +_VALID_CONTROLLED_LABELS = ['canonical'] class DataLink: ''' @@ -25,6 +27,7 @@ class DataLink: :ivar created_by: the user that created the link. :ivar expired: the expiration time or None if the link is not expired. :ivar expired_by: the user that expired the link or None if the link is not expired. + :ivar controlled_labels: Controlled vocabulary labels for this link. ''' def __init__( @@ -35,7 +38,8 @@ def __init__( created: datetime.datetime, created_by: UserID, expired: datetime.datetime = None, - expired_by: UserID = None): + expired_by: UserID = None, + controlled_labels: Optional[List[str]] = None): ''' Create the link. If expired is provided expired_by must also be provided. If expired is falsy expired_by is ignored. @@ -47,6 +51,7 @@ def __init__( :param created_by: the user that created the link. :param expired: the expiration time for the link or None if the link is not expired. :param expired_by: the user that expired the link or None if the link is not expired. + :param controlled_labels: Controlled vocabulary labels for this link. ''' # may need to make this non ws specific. YAGNI for now. self.id = _not_falsy(id_, 'id_') @@ -56,6 +61,7 @@ def __init__( self.created_by = _not_falsy(created_by, 'created_by') self.expired = None self.expired_by = None + self.controlled_labels = DataLink.validate_controlled_labels(controlled_labels) if expired: self.expired = _check_timestamp(expired, 'expired') if expired < created: @@ -93,3 +99,20 @@ def __eq__(self, other): def __hash__(self): return hash((self.id, self.duid, self.sample_node_address, self.created, self.created_by, self.expired, self.expired_by)) + + @staticmethod + def validate_controlled_labels(labels: List[str] | None): + ''' + Validate the controlled vocabulary labels. + + :param labels: the labels to validate. + :returns: the validated labels. + ''' + if not labels: + return [] + normalized = [label.strip().lower() for label in labels] + bad_labels = [label for label in normalized if label not in _VALID_CONTROLLED_LABELS] + if bad_labels: + raise ValueError(f'invalid controlled vocabulary labels: {bad_labels}.'+ + f'Valid labels are: {_VALID_CONTROLLED_LABELS}') + return labels diff --git a/lib/SampleService/core/samples.py b/lib/SampleService/core/samples.py index 777e0359..c2af7831 100644 --- a/lib/SampleService/core/samples.py +++ b/lib/SampleService/core/samples.py @@ -352,6 +352,7 @@ def create_data_link( user: UserID, duid: DataUnitID, sna: SampleNodeAddress, + labels: List[str], update: bool = False, as_admin: bool = False) -> DataLink: ''' @@ -367,6 +368,7 @@ def create_data_link( :param user: the user creating the link. :param duid: the data unit to link the the sample. :param sna: the sample node to link to the data unit. + :param labels: the labels to apply to the link. :param update: True to expire any extant link if it does not link to the provided sample. If False and a link from the data unit already exists, link creation will fail. :param as_admin: allow link creation to proceed if user does not have @@ -388,7 +390,7 @@ def create_data_link( _not_falsy(sna, 'sna').sampleid, user, _SampleAccessType.ADMIN, as_admin=as_admin) wsperm = _WorkspaceAccessType.NONE if as_admin else _WorkspaceAccessType.WRITE self._ws.has_permission(user, wsperm, upa=duid.upa) - dl = DataLink(self._uuid_gen(), duid, sna, self._now(), user) + dl = DataLink(self._uuid_gen(), duid, sna, self._now(), user, controlled_labels=labels) expired_id = self._storage.create_data_link(dl, update=update) if self._kafka: self._kafka.notify_new_link(dl.id) @@ -396,6 +398,29 @@ def create_data_link( self._kafka.notify_expired_link(expired_id) return dl + def label_data_links(self, user: UserID, duids: List[DataUnitID], add_labels: List[str], remove_labels: List[str], as_admin: bool = False) -> None: + ''' + Label a list of data links. The user must have admin access to the sample, + since labeling data grants permissions: once labeled, if a user + has access to the data unit, the user also has access to the sample. + + :param user: the user labeling the links. + :param links: the links to label. + :param as_admin: allow label creation to proceed if user does not + ''' + _not_falsy(user, 'user') + _not_falsy(duids, 'duids') + wsperm = _WorkspaceAccessType.NONE if as_admin else _WorkspaceAccessType.WRITE + + # check permissions on the links' data objects + # as a set so we dont check permissions on the same workspace twice + required_workspaces = set(duid.upa.wsid for duid in duids) + for ws_id in required_workspaces: + self._ws.has_permission(user, wsperm, workspace_id=ws_id) + + self._storage.label_data_links(duids, add_labels, remove_labels) + + def expire_data_link(self, user: UserID, duid: DataUnitID, as_admin: bool = False) -> None: ''' Expire a data link, ensuring that it will not show up in link queries without an effective diff --git a/lib/SampleService/core/storage/arango_sample_storage.py b/lib/SampleService/core/storage/arango_sample_storage.py index 0009a8c9..616bde75 100644 --- a/lib/SampleService/core/storage/arango_sample_storage.py +++ b/lib/SampleService/core/storage/arango_sample_storage.py @@ -159,6 +159,7 @@ _FLD_LINK_CREATED_BY = 'createby' _FLD_LINK_EXPIRED = 'expired' _FLD_LINK_EXPIRED_BY = 'expireby' +_FLD_LINK_CONTROLLED_LABELS = 'clabels' # see https://www.arangodb.com/2018/07/time-traveling-with-graph-databases/ _ARANGO_MAX_INTEGER = 2**53 - 1 @@ -1364,7 +1365,8 @@ def _create_link_doc(self, link: DataLink, samplever: UUID): # recording the integer version saves looking it up in the version doc and it's # immutable so denormalization is ok here _FLD_LINK_SAMPLE_INT_VERSION: sna.version, - _FLD_LINK_SAMPLE_NODE: sna.node + _FLD_LINK_SAMPLE_NODE: sna.node, + _FLD_LINK_CONTROLLED_LABELS: link.controlled_labels, } def _get_link_doc_from_link_id(self, id_): @@ -1388,6 +1390,42 @@ def _get_link_doc_from_duid(self, duid): raise _NoSuchLinkError(str(duid)) return linkdoc + def label_data_links( + self, + duids: List[DataUnitID], + add_labels: List[str], + remove_labels: List[str]) -> List[DataLink]: + '''Set or remove labels from a data link.''' + # validate labels to be added + normalized_add_labels = DataLink.validate_controlled_labels(add_labels) + # create transaction + tdb = self._db.begin_transaction( + read=self._col_data_link.name, + write=self._col_data_link.name) + try: + tdlc = tdb.collection(self._col_data_link.name) + linkdocs = [] + for duid in duids: + linkdoc = self._get_link_doc_from_duid(duid) + labels = linkdoc[_FLD_LINK_CONTROLLED_LABELS] + # add and remove labels from doc + for rem in remove_labels: + if rem in labels: + labels.remove(rem) + for add in normalized_add_labels: + if add not in labels: + labels.append(add) + # update the link doc (in transaction) + tdlc.update(linkdoc) + linkdocs.append(linkdoc) + # nothing thrown, so commit the transaction + self._commit_transaction(tdb) + return linkdocs + finally: + # rollback if an exception was thrown + self._abort_transaction(tdb) + + def expire_data_link( self, expired: datetime.datetime, @@ -1509,7 +1547,8 @@ def _doc_to_link(self, doc) -> DataLink: self._timestamp_to_datetime(self._timestamp_milliseconds_to_seconds(doc[_FLD_LINK_CREATED])), UserID(doc[_FLD_LINK_CREATED_BY]), None if ex == _ARANGO_MAX_INTEGER else self._timestamp_to_datetime(self._timestamp_milliseconds_to_seconds(ex)), - UserID(doc[_FLD_LINK_EXPIRED_BY]) if doc[_FLD_LINK_EXPIRED_BY] else None + UserID(doc[_FLD_LINK_EXPIRED_BY]) if doc[_FLD_LINK_EXPIRED_BY] else None, + doc.get(_FLD_LINK_CONTROLLED_LABELS, []) ) def _doc_to_dataunit_id(self, doc) -> DataUnitID: diff --git a/test/core/storage/arango_sample_storage_test.py b/test/core/storage/arango_sample_storage_test.py index af9e8337..1a350fa9 100644 --- a/test/core/storage/arango_sample_storage_test.py +++ b/test/core/storage/arango_sample_storage_test.py @@ -1733,7 +1733,8 @@ def test_create_and_get_data_link(samplestorage): 'created': 500000, 'createby': 'usera', 'expired': 9007199254740991, - 'expireby': None + 'expireby': None, + 'clabels': [] } link2 = samplestorage._col_data_link.get('42_42_42_bc7324de86d54718dd0dc29c55c6d53a') @@ -1755,7 +1756,8 @@ def test_create_and_get_data_link(samplestorage): 'created': 600000, 'createby': 'userb', 'expired': 9007199254740991, - 'expireby': None + 'expireby': None, + 'clabels': [] } link3 = samplestorage._col_data_link.get('5_89_32_3735ce9bbe59e7ec245da484772f9524') @@ -1777,7 +1779,8 @@ def test_create_and_get_data_link(samplestorage): 'created': 700000, 'createby': 'u', 'expired': 9007199254740991, - 'expireby': None + 'expireby': None, + 'clabels': [] } link4 = samplestorage._col_data_link.get('5_89_32_bc7324de86d54718dd0dc29c55c6d53a') @@ -1799,7 +1802,8 @@ def test_create_and_get_data_link(samplestorage): 'created': 800000, 'createby': 'userd', 'expired': 9007199254740991, - 'expireby': None + 'expireby': None, + 'clabels': [] } # test get method @@ -1901,7 +1905,8 @@ def test_creaate_data_link_with_update_no_extant_link(samplestorage): 'created': 500000, 'createby': 'usera', 'expired': 9007199254740991, - 'expireby': None + 'expireby': None, + 'clabels': [] } link2 = samplestorage._col_data_link.get('5_89_32_bc7324de86d54718dd0dc29c55c6d53a') @@ -1923,7 +1928,8 @@ def test_creaate_data_link_with_update_no_extant_link(samplestorage): 'created': 550000, 'createby': 'user', 'expired': 9007199254740991, - 'expireby': None + 'expireby': None, + 'clabels': [] } # test get method @@ -2021,7 +2027,8 @@ def test_create_data_link_with_update_noop(samplestorage): 'created': 500000, 'createby': 'usera', 'expired': 9007199254740991, - 'expireby': None + 'expireby': None, + 'clabels': [] } link2 = samplestorage._col_data_link.get('5_89_32_bc7324de86d54718dd0dc29c55c6d53a') @@ -2043,7 +2050,8 @@ def test_create_data_link_with_update_noop(samplestorage): 'created': 550000, 'createby': 'user', 'expired': 9007199254740991, - 'expireby': None + 'expireby': None, + 'clabels': [] } # test get method @@ -2138,7 +2146,8 @@ def test_create_data_link_with_update(samplestorage): 'created': 500000, 'createby': 'usera', 'expired': 599999, - 'expireby': 'userb' + 'expireby': 'userb', + 'clabels': [] } link2 = samplestorage._col_data_link.get('5_89_32_bc7324de86d54718dd0dc29c55c6d53a_550.0') @@ -2160,7 +2169,8 @@ def test_create_data_link_with_update(samplestorage): 'created': 550000, 'createby': 'user', 'expired': 699999, - 'expireby': 'userc' + 'expireby': 'userc', + 'clabels': [] } link3 = samplestorage._col_data_link.get('5_89_32') @@ -2182,7 +2192,8 @@ def test_create_data_link_with_update(samplestorage): 'created': 600000, 'createby': 'userb', 'expired': 9007199254740991, - 'expireby': None + 'expireby': None, + 'clabels': [] } link4 = samplestorage._col_data_link.get('5_89_32_bc7324de86d54718dd0dc29c55c6d53a') @@ -2204,7 +2215,8 @@ def test_create_data_link_with_update(samplestorage): 'created': 700000, 'createby': 'userc', 'expired': 9007199254740991, - 'expireby': None + 'expireby': None, + 'clabels': [] } # test get method. Expired, so DUID won't work here @@ -2954,7 +2966,8 @@ def _expire_and_get_data_link_via_duid(samplestorage, expired, dataid, expectedm 'created': -100000, 'createby': 'userb', 'expired': expired * 1000, - 'expireby': 'yay' + 'expireby': 'yay', + 'clabels': [] } assert samplestorage.get_data_link(lid) == DataLink( @@ -3025,7 +3038,8 @@ def _expire_and_get_data_link_via_id(samplestorage, expired, dataid, expectedmd5 'created': 5000, 'createby': 'usera', 'expired': expired * 1000, - 'expireby': 'user' + 'expireby': 'user', + 'clabels': [] } link = samplestorage.get_data_link(lid)