Skip to content
This repository was archived by the owner on Jan 19, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ data class MoveAnnotation(val destination: String) : EditorAnnotation() {
override val validTargets = GLOBAL_DECLARATIONS
}

@Serializable
object OmittedAnnotation : EditorAnnotation() {
@Transient
override val validTargets = PARAMETERS
}

@Serializable
data class OptionalAnnotation(val defaultValue: DefaultValue) : EditorAnnotation() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.larsreimann.apiEditor.model.DefaultNone
import com.larsreimann.apiEditor.model.DefaultNumber
import com.larsreimann.apiEditor.model.DefaultString
import com.larsreimann.apiEditor.model.DefaultValue
import com.larsreimann.apiEditor.model.OmittedAnnotation
import com.larsreimann.apiEditor.model.OptionalAnnotation
import com.larsreimann.apiEditor.model.PythonParameterAssignment
import com.larsreimann.apiEditor.model.RequiredAnnotation
Expand Down Expand Up @@ -39,6 +40,7 @@ private fun PythonParameter.processValueAnnotations() {
.forEach {
when (it) {
is ConstantAnnotation -> processConstantAnnotation(it)
is OmittedAnnotation -> processOmittedAnnotation(it)
is OptionalAnnotation -> processOptionalAnnotation(it)
is RequiredAnnotation -> processRequiredAnnotation(it)
else -> {}
Expand All @@ -61,6 +63,30 @@ private fun PythonParameter.processConstantAnnotation(annotation: ConstantAnnota

// Remove parameter
this.release()

// Remove annotation
this.annotations.remove(annotation)
}

private fun PythonParameter.processOmittedAnnotation(annotation: OmittedAnnotation) {
// Update argument that references this parameter
val arguments = crossReferencesToThis()
.mapNotNull { (it.parent as? PythonReference)?.closest<PythonArgument>() }
.toList()

require(arguments.size == 1) {
"Expected parameter to be referenced in exactly one argument but was used in $arguments."
}

// Remove argument
val argument = arguments[0]
argument.release()

// Remove parameter
this.release()

// Remove annotation
this.annotations.remove(annotation)
}

private fun PythonParameter.processOptionalAnnotation(annotation: OptionalAnnotation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.larsreimann.apiEditor.transformation
import com.larsreimann.apiEditor.model.ConstantAnnotation
import com.larsreimann.apiEditor.model.DefaultBoolean
import com.larsreimann.apiEditor.model.DefaultNone
import com.larsreimann.apiEditor.model.OmittedAnnotation
import com.larsreimann.apiEditor.model.OptionalAnnotation
import com.larsreimann.apiEditor.model.PythonParameterAssignment
import com.larsreimann.apiEditor.model.RequiredAnnotation
Expand Down Expand Up @@ -97,11 +98,32 @@ class ValueAnnotationProcessorTest {

testPackage.processValueAnnotations()

testPackage.annotations
testParameter.annotations
.filterIsInstance<ConstantAnnotation>()
.shouldBeEmpty()
}

@Test
fun `should process OmittedAnnotations`() {
testParameter.annotations += OmittedAnnotation

testPackage.processValueAnnotations()

testMethod.parameters.shouldBeEmpty()
testMethod.callToOriginalAPI?.arguments.shouldBeEmpty()
}

@Test
fun `should remove OmittedAnnotations`() {
testParameter.annotations += OmittedAnnotation

testPackage.processValueAnnotations()

testParameter.annotations
.filterIsInstance<OmittedAnnotation>()
.shouldBeEmpty()
}

@Test
fun `should process OptionalAnnotations`() {
testParameter.annotations += OptionalAnnotation(DefaultBoolean(true))
Expand All @@ -118,7 +140,7 @@ class ValueAnnotationProcessorTest {

testPackage.processValueAnnotations()

testPackage.annotations
testParameter.annotations
.filterIsInstance<OptionalAnnotation>()
.shouldBeEmpty()
}
Expand All @@ -141,7 +163,7 @@ class ValueAnnotationProcessorTest {

testPackage.processValueAnnotations()

testPackage.annotations
testParameter.annotations
.filterIsInstance<RequiredAnnotation>()
.shouldBeEmpty()
}
Expand Down
4 changes: 3 additions & 1 deletion api-editor/gui/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ export const App: React.FC = function () {
<RenameForm target={userActionTarget || rawPythonPackage} />
)}
{currentUserAction.type === 'todo' && <TodoForm target={userActionTarget || rawPythonPackage} />}
{currentUserAction.type === 'value' && <ValueForm target={userActionTarget || rawPythonPackage} />}
{currentUserAction.type === 'value' && userActionTarget instanceof PythonParameter && (
<ValueForm target={userActionTarget} />
)}
</GridItem>
<GridItem gridArea="middlePane" overflow="auto" display="flex">
<Box flexGrow={1} overflowY="auto" width="100%">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const AchievementDisplay: React.FC = function () {
<AchievementCard
currentCount={auditorCount}
achievement={auditorAchievement}
description={`${pluralize(auditorCount, 'annotation')} marked as correct`}
description={`${pluralize(auditorCount, 'annotation')} reviewed`}
/>
<AchievementCard
currentCount={authorCount}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export class AnnotatedPythonPackageBuilder {
'Enum',
'Groups',
'Move',
'Omitted',
'Optional',
'Pure',
'Remove',
Expand Down Expand Up @@ -215,10 +216,16 @@ export class AnnotatedPythonPackageBuilder {
return new InferableMoveAnnotation(moveAnnotation);
}
break;
case 'Optional':
case 'Omitted':
const valueAnnotation2 = this.annotationStore.valueAnnotations[target];
if (annotationShouldBeProcessed(valueAnnotation2) && valueAnnotation2.variant === 'optional') {
return new InferableOptionalAnnotation(valueAnnotation2);
if (annotationShouldBeProcessed(valueAnnotation2) && valueAnnotation2.variant === 'omitted') {
return new InferableRequiredAnnotation();
}
break;
case 'Optional':
const valueAnnotation3 = this.annotationStore.valueAnnotations[target];
if (annotationShouldBeProcessed(valueAnnotation3) && valueAnnotation3.variant === 'optional') {
return new InferableOptionalAnnotation(valueAnnotation3);
}
break;
case 'Pure':
Expand All @@ -240,8 +247,8 @@ export class AnnotatedPythonPackageBuilder {
}
break;
case 'Required':
const valueAnnotation3 = this.annotationStore.valueAnnotations[target];
if (annotationShouldBeProcessed(valueAnnotation3) && valueAnnotation3.variant === 'required') {
const valueAnnotation4 = this.annotationStore.valueAnnotations[target];
if (annotationShouldBeProcessed(valueAnnotation4) && valueAnnotation4.variant === 'required') {
return new InferableRequiredAnnotation();
}
break;
Expand All @@ -257,5 +264,5 @@ export class AnnotatedPythonPackageBuilder {
}

const annotationShouldBeProcessed = function (annotation: Annotation): boolean {
return annotation && !annotation.isRemoved && annotation.reviewResult === ReviewResult.Wrong;
return annotation && !annotation.isRemoved && annotation.reviewResult !== ReviewResult.Wrong;
};
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ export class InferableMoveAnnotation extends InferableAnnotation {
}
}

export class InferableOmittedAnnotation extends InferableAnnotation {
constructor() {
super(dataPathPrefix + 'OmittedAnnotation');
}
}

export class InferableOptionalAnnotation extends InferableAnnotation {
readonly defaultValue: { type: string; value?: DefaultValue };

Expand Down
26 changes: 14 additions & 12 deletions api-editor/gui/src/features/annotations/AnnotationView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -251,26 +251,28 @@ export const AnnotationView: React.FC<AnnotationViewProps> = function ({ target
};

const valueAnnotationToString = (valueAnnotation: ValueAnnotation): string => {
if (valueAnnotation.variant === 'omitted') {
return 'omitted';
} else if (valueAnnotation.variant === 'required') {
return 'required';
}

let result = '';

if (valueAnnotation.variant === 'constant') {
result += 'constant';
} else if (valueAnnotation.variant === 'optional') {
result += 'optional with default';
} else if (valueAnnotation.variant === 'required') {
result += 'required';
}

if (valueAnnotation.variant !== 'required') {
if (valueAnnotation.defaultValueType === 'string') {
result += ` "${valueAnnotation.defaultValue}"`;
} else if (valueAnnotation.defaultValueType === 'number') {
result += ' ' + String(valueAnnotation.defaultValue);
} else if (valueAnnotation.defaultValueType === 'boolean') {
result += valueAnnotation.defaultValue === true ? ' True' : ' False';
} else if (valueAnnotation.defaultValueType === 'none') {
result += ' None';
}
if (valueAnnotation.defaultValueType === 'string') {
result += ` "${valueAnnotation.defaultValue}"`;
} else if (valueAnnotation.defaultValueType === 'number') {
result += ' ' + String(valueAnnotation.defaultValue);
} else if (valueAnnotation.defaultValueType === 'boolean') {
result += valueAnnotation.defaultValue === true ? ' True' : ' False';
} else if (valueAnnotation.defaultValueType === 'none') {
result += ' None';
}

return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
Stack,
Text as ChakraText,
Textarea,
Tooltip,
} from '@chakra-ui/react';
import { useForm } from 'react-hook-form';
import { booleanPattern, numberPattern } from '../../../common/validation';
Expand All @@ -39,7 +40,7 @@ interface ValueBatchFormProps {

export const ValueBatchForm: React.FC<ValueBatchFormProps> = function ({ targets }) {
//only parameters can have optional annotations
const filteredTargets = targets.filter((t) => t instanceof PythonParameter);
const filteredTargets = targets.filter((t) => t instanceof PythonParameter) as PythonParameter[];
const targetPaths = filteredTargets.map((t) => t.id);

// Hooks -----------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -73,7 +74,7 @@ export const ValueBatchForm: React.FC<ValueBatchFormProps> = function ({ targets
};

interface TypeValueBatchFormProps {
targets: PythonDeclaration[];
targets: PythonParameter[];
description: string;
onUpsertAnnotation: (data: TypeValueBatchFormState) => void;
}
Expand Down Expand Up @@ -174,19 +175,49 @@ export const TypeValueBatchForm: React.FC<TypeValueBatchFormProps> = function ({
<FormLabel>Choose the variant of this annotation:</FormLabel>
<RadioGroup defaultValue={'optional'} onChange={handleVariantChange}>
<Stack direction="column">
<Radio value="required">Required (parameter must always be set explicitly)</Radio>
<Radio value="optional">Optional (parameter has a default value that can be overwritten)</Radio>
<Radio value="constant">Constant (parameter is replaced by a constant value)</Radio>
<Radio value="required">
<Tooltip
label="Users of the wrapper must set this parameter explicitly. This value is passed to
the original API."
>
Required
</Tooltip>
</Radio>
<Radio value="optional">
<Tooltip
label="Users of the wrapper can set this parameter explicitly. If they do, this value is
passed to the original API. Otherwise, a default value is used."
>
Optional
</Tooltip>
</Radio>
<Radio value="constant">
<Tooltip
label="This parameter no longer appears in the wrapper. Instead, a constant value is passed
to the original API."
>
Constant
</Tooltip>
</Radio>
<Radio value="omitted">
<Tooltip
label="This parameter no longer appears in the wrapper. Moreover, no value is passed to the
original API, so the original default value is used. This option is only available for
parameters that are optional in the original API."
>
Omitted
</Tooltip>
</Radio>
</Stack>
</RadioGroup>

{watchVariant !== 'required' && (
{watchVariant !== 'required' && watchVariant !== 'omitted' && (
<>
{watchVariant === 'optional' && (
<FormLabel>Type of default value of matched elements:</FormLabel>
<FormLabel>Type of the default value for matched elements:</FormLabel>
)}
{watchVariant === 'constant' && (
<FormLabel>Type of constant value of matched elements:</FormLabel>
<FormLabel>Type of the constant value for matched elements:</FormLabel>
)}
<RadioGroup defaultValue={'string'} onChange={handleTypeChange}>
<Stack direction="column">
Expand All @@ -199,7 +230,7 @@ export const TypeValueBatchForm: React.FC<TypeValueBatchFormProps> = function ({
</>
)}

{watchVariant !== 'required' && watchDefaultValueType !== 'none' && (
{watchVariant !== 'required' && watchVariant !== 'omitted' && watchDefaultValueType !== 'none' && (
<FormControl isInvalid={Boolean(errors?.defaultValue)}>
{watchVariant === 'optional' && <FormLabel>Default value for matched elements:</FormLabel>}
{watchVariant === 'constant' && <FormLabel>Constant value for matched elements:</FormLabel>}
Expand Down Expand Up @@ -244,7 +275,7 @@ export const TypeValueBatchForm: React.FC<TypeValueBatchFormProps> = function ({
</AnnotationBatchForm>
{confirmWindowVisible && (
<ConfirmAnnotations
targets={targets}
targets={watchVariant === 'omitted' ? targets.filter((it) => it.defaultValue !== null) : targets}
handleSave={() => handleSave(data)}
setConfirmVisible={setConfirmWindowVisible}
/>
Expand Down
Loading