From 1e015687a3f992c889587ef4162a10e924ae976b Mon Sep 17 00:00:00 2001 From: Nick Grosenbacher Date: Tue, 17 Oct 2023 11:20:44 -0700 Subject: [PATCH 1/2] SWC-6580 - Show derived annotations in AnnotationsTable component - AnnotationsTable now shows derived annotations and shows a warning when derived annotations may be pending. The warning allows the end user to refetch the data - Fix issue where top level null values would not be cleared before they were sent to Synapse, resulting in the update annotations call being rejected - Update Annotations form to clean the formData onBlur so UI validation is more likely to match platform validation. - Update getEntityJSON service call to allow fetching derived annotations - Update query key/invalidation behavior to handle new `includeDerivedAnnotations` boolean parameter - Do NOT fetch derived annotations in editor - Add test.todo for AnnotationsTable since the entire component is pending design review - Move AnnotationEditorUtilsTest to `src` to be typechecked - Update many broken REST docs links --- .../AnnotationEditorUtils.test.ts | 30 +++++-- .../AnnotationEditorUtils.ts | 14 ++++ .../SchemaDrivenAnnotationEditor.tsx | 52 ++++++++++-- ...ivenAnnotationsEditor.integration.test.tsx | 27 ++++++ .../widget/SelectWidget.tsx | 2 +- .../AnnotationsTable.integration.test.tsx | 2 + .../entity/metadata/AnnotationsTable.tsx | 84 +++++++++++++++---- .../src/synapse-client/SynapseClient.ts | 29 ++++--- .../src/synapse-queries/KeyFactory.ts | 6 +- .../src/synapse-queries/entity/useEntity.ts | 13 ++- .../src/utils/functions/EntityTypeUtils.ts | 2 +- packages/synapse-types/src/Entity/Entity.ts | 2 +- packages/synapse-types/src/OAuthClient.ts | 2 +- packages/synapse-types/src/Table/Dataset.ts | 2 +- .../synapse-types/src/Table/QueryFilter.ts | 2 +- .../synapse-types/src/Table/SubmissionView.ts | 2 +- packages/synapse-types/src/Table/Table.ts | 2 +- .../synapse-types/src/Table/TableEntity.ts | 2 +- packages/synapse-types/src/Table/View.ts | 2 +- packages/synapse-types/src/TrashedEntity.ts | 2 +- 20 files changed, 223 insertions(+), 56 deletions(-) rename packages/synapse-react-client/{test/containers/entity/annotations => src/components/SchemaDrivenAnnotationEditor}/AnnotationEditorUtils.test.ts (83%) diff --git a/packages/synapse-react-client/test/containers/entity/annotations/AnnotationEditorUtils.test.ts b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.test.ts similarity index 83% rename from packages/synapse-react-client/test/containers/entity/annotations/AnnotationEditorUtils.test.ts rename to packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.test.ts index 297ab3bf4e..d02bfa8a6d 100644 --- a/packages/synapse-react-client/test/containers/entity/annotations/AnnotationEditorUtils.test.ts +++ b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.test.ts @@ -1,12 +1,28 @@ -import { AjvError } from '@rjsf/core' +import { RJSFValidationError } from '@rjsf/utils' import { dropNullishArrayValues, + dropNullValues, getFriendlyPropertyName, getTransformErrors, -} from '../../../../src/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils' +} from './AnnotationEditorUtils' import { FILE_ENTITY_CONCRETE_TYPE_VALUE } from '@sage-bionetworks/synapse-types' describe('AnnotationEditorUtils tests', () => { + describe('dropNullValues', () => { + it('removes null values only', () => { + const formData = { + a: 'foo', + b: {}, + c: [], + d: '', + e: undefined, + f: null, + } + const expected = { a: 'foo', b: {}, c: [], d: '', e: undefined } + expect(dropNullValues(formData)).toEqual(expected) + }) + }) + describe('dropNullishArrayValues', () => { it('will remove an empty array from the formData', () => { const formData = { @@ -33,7 +49,8 @@ describe('AnnotationEditorUtils tests', () => { describe('getFriendlyPropertyName', () => { it('strips the leading period from a schema-defined property', () => { - const error = { + const error: RJSFValidationError = { + stack: '.study[0] is a requiredProperty', property: '.study[0]', } @@ -42,7 +59,8 @@ describe('AnnotationEditorUtils tests', () => { expect(getFriendlyPropertyName(error)).toEqual(expected) }) it('parses additionalProperties from an error', () => { - const error = { + const error: RJSFValidationError = { + stack: '.study[0] is a requiredProperty', property: "['myProperty']", } @@ -55,7 +73,7 @@ describe('AnnotationEditorUtils tests', () => { describe('transformErrors', () => { it('combines errors caused by an enumeration defined using anyOf', () => { const transformErrors = getTransformErrors() - const errors: AjvError[] = [ + const errors: RJSFValidationError[] = [ { name: 'type', property: '.study[0]', @@ -105,7 +123,7 @@ describe('AnnotationEditorUtils tests', () => { const transformErrors = getTransformErrors( FILE_ENTITY_CONCRETE_TYPE_VALUE, ) - const errors: AjvError[] = [ + const errors: RJSFValidationError[] = [ { name: 'not', property: "['dataFileHandleId']", diff --git a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.ts b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.ts index 1a66d84c9f..8269fd097e 100644 --- a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.ts +++ b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/AnnotationEditorUtils.ts @@ -27,6 +27,20 @@ export function dropNullishArrayValues( return newFormData } +export function dropNullValues( + formData: Record = {}, +): Record { + return Object.keys(formData).reduce( + (acc: Record, key: string) => { + if (formData[key] !== null) { + acc[key] = formData[key] + } + return acc + }, + {}, + ) +} + /** * Inspects the property of the AjvError and modifies it to be comparable to simple key strings, like entity property keys. * @param error diff --git a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationEditor.tsx b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationEditor.tsx index 4a6ab47560..977e75ec92 100644 --- a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationEditor.tsx +++ b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationEditor.tsx @@ -14,7 +14,7 @@ import { useGetSchemaBinding, useUpdateViaJson, } from '../../synapse-queries' -import { SynapseClientError } from '../../utils/SynapseClientError' +import { SynapseClientError } from '../../utils' import { ENTITY_CONCRETE_TYPE, EntityJson, @@ -23,6 +23,7 @@ import { SynapseSpinner } from '../LoadingScreen/LoadingScreen' import { AdditionalPropertiesSchemaField } from './field/AdditionalPropertiesSchemaField' import { dropNullishArrayValues, + dropNullValues, getFriendlyPropertyName, getTransformErrors, } from './AnnotationEditorUtils' @@ -67,7 +68,7 @@ export type SchemaDrivenAnnotationEditorProps = { /** If defined and formRef is not supplied, shows a 'Cancel' button and runs this effect on click */ onCancel?: () => void /** Passes new form data upon each change to the form */ - onChange?: (annotations: Record) => void + onChange?: (annotations: Record) => void } /** @@ -91,6 +92,22 @@ function getPatternPropertiesBannedKeys( ) } +/** + * Cleans the formData to remove values that are invalid for Synapse Annotations + * @param formData + * @param dropNullishValuesInArrays + */ +function cleanFormData( + formData: Record | undefined, + dropNullishValuesInArrays: boolean, +) { + let cleanedFormData = dropNullValues(formData) + if (dropNullishValuesInArrays) { + cleanedFormData = dropNullishArrayValues(cleanedFormData) + } + return cleanedFormData +} + /** * Renders a form for editing an entity's annotations. The component also supports supplying just a schema ID, * but work to support annotation flows without an entity (i.e. before creating entities) is not yet complete. @@ -126,12 +143,17 @@ export function SchemaDrivenAnnotationEditor( const [showConfirmation, setShowConfirmation] = React.useState(false) - const { entityMetadata: entityJson, annotations } = useGetJson(entityId!, { - // Metadata is being edited, so don't refetch - staleTime: Infinity, - enabled: !!entityId, - useErrorBoundary: true, - }) + const { entityMetadata: entityJson, annotations } = useGetJson( + entityId!, + // Derived annotations will be precomputed and displayed as placeholders in the form + false, + { + // Metadata is being edited, so don't refetch + staleTime: Infinity, + enabled: !!entityId, + useErrorBoundary: true, + }, + ) // Annotation fields fetched and modified via the form const [formData, setFormData] = React.useState< @@ -190,7 +212,7 @@ export function SchemaDrivenAnnotationEditor( function submitChangedEntity() { mutation.mutate({ - ...dropNullishArrayValues(formData ?? {}), + ...cleanFormData(formData, true), ...entityJson, } as EntityJson) } @@ -294,8 +316,20 @@ export function SchemaDrivenAnnotationEditor( setFormData(formData) setValidationError(undefined) }} + onBlur={() => { + setFormData( + // Clean the formData onBlur to remove null values that we will need to strip before submission + // This will ensure that the user gets accurate validation information since the data will match what the backend will receive + cleanFormData( + formData, + // Don't remove null values in arrays--the fields will disappear, which the user probably does not want + false, + ), + ) + }} onSubmit={({ formData, errors }, event) => { event.preventDefault() + if (errors && errors.length > 0) { setValidationError(errors) } diff --git a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationsEditor.integration.test.tsx b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationsEditor.integration.test.tsx index 018f4854b3..85a94d64b6 100644 --- a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationsEditor.integration.test.tsx +++ b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationsEditor.integration.test.tsx @@ -700,6 +700,33 @@ describe('SchemaDrivenAnnotationEditor tests', () => { ) }) + it('Does not submit null data for schema-defined properties', async () => { + server.use( + annotationsWithSchemaHandler, + ...schemaHandlers, + successfulUpdateHandler, + ) + await renderComponent() + await screen.findByText('requires scientific annotations', { exact: false }) + + const countryField = await screen.findByLabelText('country*') + + // Pick a value to ensure the property exists in the formData + await chooseAutocompleteOption(countryField, 'USA') + + // Now clear the field + await userEvent.click(await screen.findByLabelText('Clear')) + + // Save the form + await clickSaveAndConfirm() + + await waitFor(() => expect(updatedJsonCaptor).toHaveBeenCalled()) + // Because we cleared the field, country should not exist in the payload + expect(Object.hasOwn(updatedJsonCaptor.mock.calls[0][0], 'country')).toBe( + false, + ) + }) + it('Initializes an empty array but does not submit null data', async () => { server.use( emptyArrayAnnotationsHandler, // showStringArray will be true but stringArray will have no data diff --git a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/widget/SelectWidget.tsx b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/widget/SelectWidget.tsx index 98de988a13..2b45573c89 100644 --- a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/widget/SelectWidget.tsx +++ b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/widget/SelectWidget.tsx @@ -69,7 +69,7 @@ export const SelectWidget: Widget = (props: SelectWidgetProps) => { disabled={disabled} multiple={multiple} disableClearable={!isClearable} - onChange={(event, newValue) => { + onChange={(event, newValue, reason) => { if (isObject(newValue) && 'inputValue' in newValue) { // Create a new value from the user input onChange(newValue.inputValue) diff --git a/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.integration.test.tsx b/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.integration.test.tsx index b0338b7592..6d48b198ef 100644 --- a/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.integration.test.tsx +++ b/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.integration.test.tsx @@ -75,4 +75,6 @@ describe('AnnotationsTable tests', () => { renderComponent() await screen.findByText('This Project has no annotations.') }) + + it.todo('Handles scenario where derived annotations are pending') }) diff --git a/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.tsx b/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.tsx index 2710296d73..491618b48d 100644 --- a/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.tsx +++ b/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useCallback, useState } from 'react' import { isEmpty } from 'lodash-es' import { convertToEntityType, @@ -8,32 +8,69 @@ import { BackendDestinationEnum, getEndpoint, } from '../../../utils/functions/getEndpoint' -import { useGetSchemaBinding } from '../../../synapse-queries/entity/useEntityBoundSchema' -import { useSynapseContext } from '../../../utils/context/SynapseContext' -import { useGetJson } from '../../../synapse-queries/entity/useEntity' -import { SkeletonTable } from '../../Skeleton/SkeletonTable' +import { + useGetJson, + useGetSchemaBinding, + useGetValidationResults, +} from '../../../synapse-queries' +import { useSynapseContext } from '../../../utils' +import { SkeletonTable } from '../../Skeleton' +import dayjs from 'dayjs' +import FullWidthAlert from '../../FullWidthAlert' export type AnnotationsTableProps = { readonly entityId: string readonly versionNumber?: number } -export const AnnotationsTable: React.FC = ({ - entityId, -}) => { +export function AnnotationsTable(props: AnnotationsTableProps) { + const { entityId } = props + const [isManuallyRefetching, setIsManuallyRefetching] = useState(false) /** * Currently, schema/validation features are only shown in experimental mode. */ const { isInExperimentalMode } = useSynapseContext() // TODO: Support versioned annotations, see PLFM-7290 - const { entityMetadata, annotations, isLoading } = useGetJson(entityId) + const { + entityMetadata, + annotations, + isLoading, + refetch: refetchEntityData, + } = useGetJson(entityId, true) const { data: boundSchema } = useGetSchemaBinding(entityId, { enabled: isInExperimentalMode, }) - return isLoading ? ( + const { data: validationResults, refetch: refetchValidationInformation } = + useGetValidationResults(entityId, { + enabled: isInExperimentalMode && Boolean(boundSchema), + }) + + const showSchemaInformation = isInExperimentalMode && Boolean(boundSchema) + + // If the entity has not yet been validated since the last fetch, then derived annotations may not have been calculated. + const recentChangesHaveBeenValidated = + !!entityMetadata && + !!validationResults && + dayjs(entityMetadata.modifiedOn).diff( + dayjs(validationResults.validatedOn), + ) > 0 + + const onRefetch = useCallback(async () => { + setIsManuallyRefetching(true) + const promises = [ + // Refetch the annotations, which may have changed if new derived annotations have been calculated + refetchEntityData(), + // Refetch the validation information, which we use to determine if derived annotations may still be pending + refetchValidationInformation(), + ] + await Promise.allSettled(promises) + setIsManuallyRefetching(false) + }, [refetchEntityData, refetchValidationInformation]) + + return isLoading || isManuallyRefetching ? ( ) : ( <> @@ -63,7 +100,7 @@ export const AnnotationsTable: React.FC = ({ ) })} - {boundSchema && isInExperimentalMode ? ( + {showSchemaInformation ? ( Validation Schema @@ -72,19 +109,38 @@ export const AnnotationsTable: React.FC = ({ - {boundSchema.jsonSchemaVersionInfo.schemaName} + {boundSchema!.jsonSchemaVersionInfo.schemaName} ) : null} + {entityMetadata && + showSchemaInformation && + recentChangesHaveBeenValidated && ( + { + onRefetch() + }, + }} + isGlobal={false} + /> + )} ) } diff --git a/packages/synapse-react-client/src/synapse-client/SynapseClient.ts b/packages/synapse-react-client/src/synapse-client/SynapseClient.ts index 775bbae2de..2775a24ecf 100644 --- a/packages/synapse-react-client/src/synapse-client/SynapseClient.ts +++ b/packages/synapse-react-client/src/synapse-client/SynapseClient.ts @@ -2534,7 +2534,7 @@ export const isOAuthClientReverificationRequired = ( Get a secret credential to use when requesting an access token. Synapse supports 'client_secret_basic' and 'client_secret_post'. NOTE: This request will invalidate any previously issued secrets. -https://docs.synapse.org/rest/POST/oauth2/client/secret/id.html +https://rest-docs.synapse.org/rest/POST/oauth2/client/secret/id.html */ export const createOAuthClientSecret = ( accessToken: string, @@ -3443,7 +3443,7 @@ export const getSubmissionById = ( * Request to update a submission' state. Only ACT members and delegates with the REVIEW_SUBMISSION ACL * permission can perform this action. * - * See https://docs.synapse.org/rest/PUT/dataAccessSubmission/submissionId.html + * See https://rest-docs.synapse.org/rest/PUT/dataAccessSubmission/submissionId.html * @param request * @param accessToken * @returns @@ -3462,7 +3462,7 @@ export const updateSubmissionStatus = ( /** * Get the schema bound to an entity. - * https://docs.synapse.org/rest/GET/entity/id/schema/binding.html + * https://rest-docs.synapse.org/rest/GET/entity/id/schema/binding.html * @param entityId * @param accessToken * @returns @@ -3480,7 +3480,7 @@ export const getSchemaBinding = (entityId: string, accessToken?: string) => { /** * Get the schema bound to an entity. - * https://docs.synapse.org/rest/GET/entity/id/schema/binding.html + * https://rest-docs.synapse.org/rest/GET/entity/id/schema/binding.html * @param entityId * @param accessToken * @returns @@ -3498,7 +3498,7 @@ export const getSchemaValidationResults = ( /** * Get a schema by its $id. - * https://docs.synapse.org/rest/GET/entity/id/schema/binding.html + * https://rest-docs.synapse.org/rest/GET/entity/id/schema/binding.html * @returns */ export const getSchema = (schema$id: string) => { @@ -3540,7 +3540,7 @@ export const getValidationSchema = async ( /** * Determine if the caller has a particular access type on an entity - * https://docs.synapse.org/rest/GET/entity/id/access.html + * https://rest-docs.synapse.org/rest/GET/entity/id/access.html * @param entityId * @param accessToken * @returns @@ -3559,14 +3559,21 @@ export const hasAccessToEntity = ( /** * Get the entity and its annotations as a JSON object - * https://docs.synapse.org/rest/GET/entity/id/json.html + * https://rest-docs.synapse.org/rest/GET/entity/id/json.html * @param entityId + * @param includeDerivedAnnotations * @param accessToken * @returns */ -export const getEntityJson = (entityId: string, accessToken?: string) => { +export const getEntityJson = ( + entityId: string, + includeDerivedAnnotations: boolean, + accessToken?: string, +) => { + const params = new URLSearchParams() + params.set('includeDerivedAnnotations', String(includeDerivedAnnotations)) return doGet( - ENTITY_JSON(entityId), + `${ENTITY_JSON(entityId)}?${params.toString()}`, accessToken, BackendDestinationEnum.REPO_ENDPOINT, ) @@ -3574,7 +3581,7 @@ export const getEntityJson = (entityId: string, accessToken?: string) => { /** * Update an entity and its annotations using a JSON object - * https://docs.synapse.org/rest/PUT/entity/id/json.html + * https://rest-docs.synapse.org/rest/PUT/entity/id/json.html * @param entityId * @param accessToken * @returns @@ -3596,7 +3603,7 @@ export const updateEntityJson = ( * This service returns the email used for user notifications, i.e. when a Synapse message * is sent and if the user has elected to receive messages by email, then this is the * email address at which the user will receive the message. - * https://docs.synapse.org/rest/GET/notificationEmail.html + * https://rest-docs.synapse.org/rest/GET/notificationEmail.html */ export const getNotificationEmail = (accessToken?: string) => { return doGet( diff --git a/packages/synapse-react-client/src/synapse-queries/KeyFactory.ts b/packages/synapse-react-client/src/synapse-queries/KeyFactory.ts index 6493bdf2c2..14ed316541 100644 --- a/packages/synapse-react-client/src/synapse-queries/KeyFactory.ts +++ b/packages/synapse-react-client/src/synapse-queries/KeyFactory.ts @@ -247,8 +247,10 @@ export class KeyFactory { return this.getKey(entityQueryKeyObjects.children(request, infinite)) } - public getEntityJsonQueryKey(id: string) { - return this.getKey(entityQueryKeyObjects.json(id)) + public getEntityJsonQueryKey(id: string, includeDerivedAnnotations: boolean) { + return this.getKey(entityQueryKeyObjects.json(id), { + includeDerivedAnnotations, + }) } public getEntityBundleQueryKey( diff --git a/packages/synapse-react-client/src/synapse-queries/entity/useEntity.ts b/packages/synapse-react-client/src/synapse-queries/entity/useEntity.ts index 437f1ccfbe..85be277aa7 100644 --- a/packages/synapse-react-client/src/synapse-queries/entity/useEntity.ts +++ b/packages/synapse-react-client/src/synapse-queries/entity/useEntity.ts @@ -206,12 +206,18 @@ export function removeStandardEntityFields( */ export function useGetJson( entityId: string, + includeDerivedAnnotations: boolean, options?: UseQueryOptions, ) { const { accessToken, keyFactory } = useSynapseContext() const query = useQuery( - keyFactory.getEntityJsonQueryKey(entityId), - () => SynapseClient.getEntityJson(entityId, accessToken), + keyFactory.getEntityJsonQueryKey(entityId, includeDerivedAnnotations), + () => + SynapseClient.getEntityJson( + entityId, + includeDerivedAnnotations, + accessToken, + ), options, ) @@ -256,7 +262,8 @@ export function useUpdateViaJson( await invalidateAllQueriesForEntity(queryClient, keyFactory, entityId) queryClient.setQueryData( - keyFactory.getEntityJsonQueryKey(entityId), + // This annotation data will never include derived annotations, which are calculated by the backend asynchronously + keyFactory.getEntityJsonQueryKey(entityId, false), data, ) diff --git a/packages/synapse-react-client/src/utils/functions/EntityTypeUtils.ts b/packages/synapse-react-client/src/utils/functions/EntityTypeUtils.ts index 73cc44d5c0..601f6c24b6 100644 --- a/packages/synapse-react-client/src/utils/functions/EntityTypeUtils.ts +++ b/packages/synapse-react-client/src/utils/functions/EntityTypeUtils.ts @@ -202,7 +202,7 @@ export function convertToConcreteEntityType( } /** - * https://docs.synapse.org/rest/org/sagebionetworks/repo/model/VersionableEntity.html + * https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/VersionableEntity.html * @param type * @returns */ diff --git a/packages/synapse-types/src/Entity/Entity.ts b/packages/synapse-types/src/Entity/Entity.ts index 5b904f7ced..a35db2e30e 100644 --- a/packages/synapse-types/src/Entity/Entity.ts +++ b/packages/synapse-types/src/Entity/Entity.ts @@ -71,7 +71,7 @@ export type EntityJsonValue = | boolean[] | undefined -// Entity data returned as in https://docs.synapse.org/rest/GET/entity/id/json.html +// Entity data returned as in https://rest-docs.synapse.org/rest/GET/entity/id/json.html // Not comprehensive, but we don't need it to be, since we currently only use JSON for annotations. export interface EntityJson extends Record { name: string diff --git a/packages/synapse-types/src/OAuthClient.ts b/packages/synapse-types/src/OAuthClient.ts index fbfdbfb6a5..217e8f1c12 100644 --- a/packages/synapse-types/src/OAuthClient.ts +++ b/packages/synapse-types/src/OAuthClient.ts @@ -40,7 +40,7 @@ export interface OAuthClientVerificationPrecheckResult { /** OAuth 2.0 Client ID and secret, generated when a new client is created -https://docs.synapse.org/rest/org/sagebionetworks/repo/model/oauth/OAuthClientIdAndSecret.html +https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/oauth/OAuthClientIdAndSecret.html */ export interface OAuthClientIdAndSecret { client_id: string diff --git a/packages/synapse-types/src/Table/Dataset.ts b/packages/synapse-types/src/Table/Dataset.ts index 11d2ed88e6..da0b4f42d0 100644 --- a/packages/synapse-types/src/Table/Dataset.ts +++ b/packages/synapse-types/src/Table/Dataset.ts @@ -7,7 +7,7 @@ export interface EntityRef { } /** - * https://docs.synapse.org/rest/org/sagebionetworks/repo/model/table/EntityRefCollectionView.html + * https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/EntityRefCollectionView.html */ export interface EntityRefCollectionView extends View { items?: EntityRef[] // items is undefined on new collections diff --git a/packages/synapse-types/src/Table/QueryFilter.ts b/packages/synapse-types/src/Table/QueryFilter.ts index a880ab0ca4..da4ef9109c 100644 --- a/packages/synapse-types/src/Table/QueryFilter.ts +++ b/packages/synapse-types/src/Table/QueryFilter.ts @@ -41,7 +41,7 @@ export interface ColumnMultiValueFunctionQueryFilter { values: string[] // Values to used with the filter. } -// https://docs.synapse.org/rest/org/sagebionetworks/repo/model/table/TextMatchesQueryFilter.html +// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/TextMatchesQueryFilter.html export const TEXT_MATCHES_QUERY_FILTER_CONCRETE_TYPE_VALUE = 'org.sagebionetworks.repo.model.table.TextMatchesQueryFilter' export type TEXT_MATCHES_QUERY_FILTER_CONCRETE_TYPE = diff --git a/packages/synapse-types/src/Table/SubmissionView.ts b/packages/synapse-types/src/Table/SubmissionView.ts index 9326e0324c..3354ea5122 100644 --- a/packages/synapse-types/src/Table/SubmissionView.ts +++ b/packages/synapse-types/src/Table/SubmissionView.ts @@ -8,7 +8,7 @@ export type SUBMISSION_VIEW_CONCRETE_TYPE = /** * A view of evaluation submissions whose scope is defined by the evaluation ids the submissions are part of. The user must have READ_PRIVATE_SUBMISSION access on each of the evaluations in the scope. * - * https://docs.synapse.org/rest/org/sagebionetworks/repo/model/table/SubmissionView.html + * https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/SubmissionView.html */ export interface SubmissionView extends View { concreteType: SUBMISSION_VIEW_CONCRETE_TYPE diff --git a/packages/synapse-types/src/Table/Table.ts b/packages/synapse-types/src/Table/Table.ts index e1e406fefe..add13fb2c6 100644 --- a/packages/synapse-types/src/Table/Table.ts +++ b/packages/synapse-types/src/Table/Table.ts @@ -1,7 +1,7 @@ import { VersionableEntity } from '../Entity/Entity' import { TABLE_CONCRETE_TYPE } from './ConcreteType' -// https://docs.synapse.org/rest/org/sagebionetworks/repo/model/table/Table.html +// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/Table.html export interface Table extends VersionableEntity { columnIds: string[] concreteType: TABLE_CONCRETE_TYPE diff --git a/packages/synapse-types/src/Table/TableEntity.ts b/packages/synapse-types/src/Table/TableEntity.ts index 9cded60d2c..dd17545b36 100644 --- a/packages/synapse-types/src/Table/TableEntity.ts +++ b/packages/synapse-types/src/Table/TableEntity.ts @@ -1,7 +1,7 @@ import { Table } from './Table' import { TABLE_ENTITY_CONCRETE_TYPE } from './ConcreteType' -// https://docs.synapse.org/rest/org/sagebionetworks/repo/model/table/TableEntity.html +// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/TableEntity.html export interface TableEntity extends Table { concreteType: TABLE_ENTITY_CONCRETE_TYPE } diff --git a/packages/synapse-types/src/Table/View.ts b/packages/synapse-types/src/Table/View.ts index 51171705a6..e9f2cf1a64 100644 --- a/packages/synapse-types/src/Table/View.ts +++ b/packages/synapse-types/src/Table/View.ts @@ -1,7 +1,7 @@ import { VIEW_CONCRETE_TYPE } from './ConcreteType' import { Table } from './Table' -// https://docs.synapse.org/rest/org/sagebionetworks/repo/model/table/View.html +// https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/table/View.html // The View interface only exists in // eslint-disable-next-line @typescript-eslint/no-empty-interface diff --git a/packages/synapse-types/src/TrashedEntity.ts b/packages/synapse-types/src/TrashedEntity.ts index c0ed2332ad..1437847c87 100644 --- a/packages/synapse-types/src/TrashedEntity.ts +++ b/packages/synapse-types/src/TrashedEntity.ts @@ -1,7 +1,7 @@ /** * JSON schema for the TrashEntity POJO. A trashed entity is an entity in the trash can. * - * https://docs.synapse.org/rest/org/sagebionetworks/repo/model/TrashedEntity.html + * https://rest-docs.synapse.org/rest/org/sagebionetworks/repo/model/TrashedEntity.html */ export type TrashedEntity = { /** The id of the entity. */ From 89760476e371f7e0dfebfa7fc1948364ace74e7e Mon Sep 17 00:00:00 2001 From: Nick Grosenbacher Date: Tue, 17 Oct 2023 11:28:35 -0700 Subject: [PATCH 2/2] Apply suggestions from code review Remove unnecessary changes, fix variable name --- .../SchemaDrivenAnnotationEditor.tsx | 1 - .../SchemaDrivenAnnotationEditor/widget/SelectWidget.tsx | 2 +- .../src/components/entity/metadata/AnnotationsTable.tsx | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationEditor.tsx b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationEditor.tsx index 977e75ec92..43c5b4d56a 100644 --- a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationEditor.tsx +++ b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/SchemaDrivenAnnotationEditor.tsx @@ -329,7 +329,6 @@ export function SchemaDrivenAnnotationEditor( }} onSubmit={({ formData, errors }, event) => { event.preventDefault() - if (errors && errors.length > 0) { setValidationError(errors) } diff --git a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/widget/SelectWidget.tsx b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/widget/SelectWidget.tsx index 2b45573c89..98de988a13 100644 --- a/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/widget/SelectWidget.tsx +++ b/packages/synapse-react-client/src/components/SchemaDrivenAnnotationEditor/widget/SelectWidget.tsx @@ -69,7 +69,7 @@ export const SelectWidget: Widget = (props: SelectWidgetProps) => { disabled={disabled} multiple={multiple} disableClearable={!isClearable} - onChange={(event, newValue, reason) => { + onChange={(event, newValue) => { if (isObject(newValue) && 'inputValue' in newValue) { // Create a new value from the user input onChange(newValue.inputValue) diff --git a/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.tsx b/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.tsx index 491618b48d..1b80899759 100644 --- a/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.tsx +++ b/packages/synapse-react-client/src/components/entity/metadata/AnnotationsTable.tsx @@ -51,7 +51,7 @@ export function AnnotationsTable(props: AnnotationsTableProps) { const showSchemaInformation = isInExperimentalMode && Boolean(boundSchema) // If the entity has not yet been validated since the last fetch, then derived annotations may not have been calculated. - const recentChangesHaveBeenValidated = + const recentChangesHaveNotBeenValidated = !!entityMetadata && !!validationResults && dayjs(entityMetadata.modifiedOn).diff( @@ -124,7 +124,7 @@ export function AnnotationsTable(props: AnnotationsTableProps) { {entityMetadata && showSchemaInformation && - recentChangesHaveBeenValidated && ( + recentChangesHaveNotBeenValidated && (