Skip to content

Commit

Permalink
Merge pull request #453 from eddbbt/server-interface-timestamp
Browse files Browse the repository at this point in the history
Remove ExplicitTimestamp from server owned interface
  • Loading branch information
Pavinati authored Jul 18, 2024
2 parents ff9fa61 + 9f732a2 commit 0478bd4
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 57 deletions.
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 0478bd4

Please sign in to comment.