Skip to content

Commit

Permalink
Remove ExplicitTimestamp from server owned interface
Browse files Browse the repository at this point in the history
Explicit timestamp is added and removed from modals and interface, depending on the selected owner, and checked at settings change

Signed-off-by: Eddy Babetto <[email protected]>
  • Loading branch information
eddbbt committed Jul 18, 2024
1 parent 517ec78 commit 3fcc34b
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 57 deletions.
1 change: 1 addition & 0 deletions cypress/fixtures/test.astarte.PropertiesInterface.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
{
"endpoint": "/%{room}/heating/active",
"type": "boolean"

},
{
"endpoint": "/lights/bath",
Expand Down
4 changes: 3 additions & 1 deletion src/astarte-client/models/Interface/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,9 @@ class AstarteInterface {
validatedObj.type === 'datastream'
? new AstarteMapping({
...mapping,
explicitTimestamp: mapping.explicitTimestamp || false,
...(validatedObj.ownership === 'device' && {
explicitTimestamp: mapping.explicitTimestamp || false,
}),
reliability: mapping.reliability || 'unreliable',
retention: mapping.retention || 'discard',
expiry: mapping.expiry || 0,
Expand Down
154 changes: 115 additions & 39 deletions src/components/InterfaceEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,13 @@ const MappingRow = ({ className, mapping, onEdit, onDelete }: MappingRowProps) =
const getDefaultMapping = (params: {
interfaceType: AstarteInterface['type'];
interfaceAggregation: AstarteInterface['aggregation'];
interfaceOwner: AstarteInterface['ownership'];
}): AstarteMapping => {
if (params.interfaceType === 'datastream' && params.interfaceAggregation === 'individual') {
if (
params.interfaceType === 'datastream' &&
params.interfaceAggregation === 'individual' &&
params.interfaceOwner == 'device'
) {
return {
endpoint: '',
type: 'double',
Expand All @@ -170,6 +175,7 @@ const getDefaultMapping = (params: {
};

interface MappingModalProps {
interfaceOwner: AstarteInterface['ownership'];
interfaceType: AstarteInterface['type'];
interfaceAggregation?: AstarteInterface['aggregation'];
mapping?: AstarteMapping;
Expand All @@ -178,14 +184,15 @@ interface MappingModalProps {
}

const MappingModal = ({
interfaceOwner,
interfaceType,
interfaceAggregation = 'individual',
mapping,
onCancel,
onConfirm,
}: MappingModalProps): React.ReactElement => {
const [mappingDraft, setMappingDraft] = useState(
mapping || getDefaultMapping({ interfaceType, interfaceAggregation }),
mapping || getDefaultMapping({ interfaceType, interfaceAggregation, interfaceOwner }),
);

const handleChange = useCallback((newMapping: AstarteMapping) => {
Expand All @@ -201,6 +208,7 @@ const MappingModal = ({
</Modal.Header>
<Modal.Body>
<MappingEditor
interfaceOwner={interfaceOwner}
interfaceType={interfaceType}
interfaceAggregation={interfaceAggregation}
mapping={mappingDraft}
Expand Down Expand Up @@ -438,25 +446,32 @@ export default ({
}, []);

const clearMappingsOptions = useCallback(
(params: { type: AstarteInterface['type']; aggregation: AstarteInterface['aggregation'] }) => {
(params: {
type: AstarteInterface['type'];
aggregation: AstarteInterface['aggregation'];
ownership: AstarteInterface['ownership'];
}) => {
setInterfaceDraft((draft) => {
const mappings = draft.mappings.map((mapping) =>
_.omit(mapping, [
const mappings = draft.mappings.map((mapping) => {
return _.omit(mapping, [
'allowUnset',
'reliability',
'retention',
'expiry',
'databaseRetentionPolicy',
'databaseRetentionTtl',
'explicitTimestamp',
]),
);
]);
});
return { ...draft, mappings };
});

if (params.type === 'datastream' && params.aggregation === 'object') {
setDatastreamOptions(defaultDatastreamOptions);
setDatastreamOptions({
...defaultDatastreamOptions,
explicitTimestamp: params.ownership === 'device',
});
} else {
setDatastreamOptions({});
setDatastreamOptions({ explicitTimestamp: params.ownership === 'device' });
}
},
[],
Expand All @@ -467,33 +482,91 @@ export default ({
const { value } = e.currentTarget;
const type = value as AstarteInterface['type'];
const aggregation = type === 'datastream' ? 'individual' : undefined;
clearMappingsOptions({ type, aggregation });
setInterfaceDraft((draft) => ({ ...draft, type, aggregation }));

clearMappingsOptions({ type, aggregation, ownership: interfaceDraft.ownership });
setInterfaceDraft((draft) => {
const mappings = draft.mappings.map((mapping) => {
if (type === 'properties') {
return _.omit(mapping, ['explicitTimestamp']) as AstarteMapping;
} else {
return {
...mapping,
explicitTimestamp: interfaceDraft.ownership === 'device',
} as AstarteMapping;
}
});

return {
...draft,
mappings,
type,
aggregation,
};
});
},
[clearMappingsOptions],
[clearMappingsOptions, interfaceDraft],
);

const handleInterfaceAggregationChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.currentTarget;
const aggregation = value as AstarteInterface['aggregation'];
clearMappingsOptions({ type: 'datastream', aggregation });
setInterfaceDraft((draft) => ({
...draft,

setInterfaceDraft((draft) => {
const mappings: AstarteMapping[] = draft.mappings.map((mapping) => {
return {
...mapping,
explicitTimestamp: draft.ownership === 'device',
} as AstarteMapping;
});

return {
...draft,
mappings,
aggregation,
};
});

clearMappingsOptions({
type: 'datastream',
aggregation,
}));
ownership: interfaceDraft.ownership,
});
},
[clearMappingsOptions],
[clearMappingsOptions, interfaceDraft],
);

const handleInterfaceOwnershipChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.currentTarget;
const ownership = value as AstarteInterface['ownership'];
setInterfaceDraft((draft) => ({
...draft,
ownership,
}));
}, []);
const handleInterfaceOwnershipChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.currentTarget;
const ownership = value as AstarteInterface['ownership'];

setInterfaceDraft((draft) => {
const mappings: AstarteMapping[] = draft.mappings.map((mapping) => {
if (interfaceDraft.type === 'properties') {
return _.omit(mapping, ['explicitTimestamp']) as AstarteMapping;
} else {
return {
...mapping,
explicitTimestamp: ownership === 'device',
} as AstarteMapping;
}
});

return {
...draft,
mappings,
ownership,
};
});
clearMappingsOptions({
type: interfaceDraft.type,
aggregation: interfaceDraft.aggregation,
ownership: ownership,
});
},
[clearMappingsOptions, interfaceDraft],
);

const handleInterfaceDescriptionChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;
Expand Down Expand Up @@ -860,19 +933,21 @@ export default ({
</Form.Select>
</Form.Group>
</Col>
<Col md={6}>
<Form.Group controlId="objectMappingExplicitTimestamp">
<Form.Label>Timestamp</Form.Label>
<Form.Check
type="checkbox"
name="mappingExplicitTimestamp"
label="Explicit timestamp"
checked={!!datastreamOptions.explicitTimestamp}
onChange={handleInterfaceExplicitTimestampChange}
disabled={denyMajorChanges}
/>
</Form.Group>
</Col>
{interfaceDraft.ownership === 'device' && (
<Col md={6}>
<Form.Group controlId="objectMappingExplicitTimestamp">
<Form.Label>Timestamp</Form.Label>
<Form.Check
type="checkbox"
name="mappingExplicitTimestamp"
label="Explicit timestamp"
checked={!!datastreamOptions.explicitTimestamp}
onChange={handleInterfaceExplicitTimestampChange}
disabled={denyMajorChanges}
/>
</Form.Group>
</Col>
)}
</Row>
)}
{interfaceDraft.type === 'datastream' && interfaceDraft.aggregation === 'object' && (
Expand Down Expand Up @@ -1043,6 +1118,7 @@ export default ({
)}
{isMappingModalVisible && (
<MappingModal
interfaceOwner={interfaceDraft.ownership}
interfaceType={interfaceDraft.type}
interfaceAggregation={interfaceDraft.aggregation}
mapping={mappingToEdit}
Expand Down
39 changes: 22 additions & 17 deletions src/components/MappingEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ const defaultMapping: AstarteMapping = {
};

interface Props {
interfaceOwner: AstarteInterface['ownership'];
interfaceType: AstarteInterface['type'];
interfaceAggregation?: AstarteInterface['aggregation'];
mapping?: AstarteMapping;
onChange: (updatedMapping: AstarteMapping) => unknown;
}

export default ({
interfaceOwner,
interfaceType,
interfaceAggregation = 'individual',
mapping = defaultMapping,
Expand All @@ -60,9 +62,9 @@ export default ({
const isPropertiesInterface = interfaceType === 'properties';
const isDatastreamIndividualInterface =
interfaceType === 'datastream' && interfaceAggregation === 'individual';
const isDevice = interfaceOwner === 'device';
const showMappingExpiry = mapping.retention === 'volatile' || mapping.retention === 'stored';
const showInterfaceDatabaseRetentionTtl = mapping.databaseRetentionPolicy === 'use_ttl';

let mappingValidationErrors: { [property: string]: string } = {};
try {
AstarteMapping.validation.validateSync(mapping, { abortEarly: false });
Expand Down Expand Up @@ -156,23 +158,26 @@ export default ({
</Form.Control.Feedback>
</Form.Group>
</Col>

<Col sm={6}>
<Form.Group controlId="mappingExplicitTimestamp">
<Form.Label>Timestamp</Form.Label>
<Form.Check
type="checkbox"
label="Explicit timestamp"
checked={!!mapping.explicitTimestamp}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
const explicitTimestamp = !!e.target.checked;
onChange({ ...mapping, explicitTimestamp: explicitTimestamp || undefined });
}}
isInvalid={mappingValidationErrors.explicitTimestamp != null}
/>
<Form.Control.Feedback type="invalid">
{mappingValidationErrors.explicitTimestamp}
</Form.Control.Feedback>
</Form.Group>
{isDevice && (
<Form.Group controlId="mappingExplicitTimestamp">
<Form.Label>Timestamp</Form.Label>
<Form.Check
type="checkbox"
label="Explicit timestamp"
checked={!!mapping.explicitTimestamp}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
const explicitTimestamp = !!e.target.checked;
onChange({ ...mapping, explicitTimestamp: explicitTimestamp || undefined });
}}
isInvalid={mappingValidationErrors.explicitTimestamp != null}
/>
<Form.Control.Feedback type="invalid">
{mappingValidationErrors.explicitTimestamp}
</Form.Control.Feedback>
</Form.Group>
)}
</Col>
</Row>
)}
Expand Down

0 comments on commit 3fcc34b

Please sign in to comment.