Skip to content

Commit

Permalink
Release 5.8.17
Browse files Browse the repository at this point in the history
### Changelog:
* Feature(backend, frontend): Added ability to pass types to DependFromFkField.
* Chore(backend): Requirements version bump.

See merge request vst/vst-utils!616
  • Loading branch information
onegreyonewhite committed Dec 6, 2023
2 parents 2d70de9 + 06735b8 commit c860660
Show file tree
Hide file tree
Showing 15 changed files with 44 additions and 70 deletions.
1 change: 1 addition & 0 deletions frontend_src/vstutils/fetch-values.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ function fetchInstancesFields(instances: Model[], fields: Field[], options?: Opt
} else if (field instanceof ArrayField) {
promises.push(fetchArrayFieldValues(field as ArrayField, instances, options));
} else if (field instanceof DynamicField) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
promises.push(fetchDynamicFieldValues(field, instances, options));
} else if (field instanceof RelatedListField) {
promises.push(fetchRelatedListFieldValues(field, instances, options));
Expand Down
58 changes: 13 additions & 45 deletions frontend_src/vstutils/fields/dynamic/DependFromFkField.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,40 @@
import { BaseField } from '@/vstutils/fields/base';
import { mergeDeep } from '@/vstutils/utils';
import DependFromFkFieldMixin from './DependFromFkFieldMixin.vue';
import type { Field, FieldOptions, FieldXOptions } from '@/vstutils/fields/base';
import type { InnerData, RepresentData } from '@/vstutils/utils';
import type { FieldOptions } from '@/vstutils/fields/base';
import type { DynamicFieldXOptions } from './DynamicField';
import { DynamicField } from './DynamicField';

interface XOptions extends FieldXOptions {
interface XOptions extends DynamicFieldXOptions {
field: string;
field_attribute: string;
callback?: (data: Record<string, unknown>) => Record<string, unknown>;
}

export class DependFromFkField extends BaseField<unknown, unknown, XOptions> {
export class DependFromFkField extends DynamicField<XOptions> {
dependField: string;
dependFieldAttribute: string;

/**
* Function that is used to customize real field options
*/
callback?: (data: Record<string, unknown>) => Record<string, unknown>;

constructor(options: FieldOptions<XOptions, unknown>) {
super(options);

this.dependField = this.props.field;
this.dependFieldAttribute = this.props.field_attribute;
this.callback = this.props.callback;
}

static get mixins() {
return [DependFromFkFieldMixin];
}

toInner(data: RepresentData) {
return this.getRealField(data).toInner(data);
}

toRepresent(data: InnerData) {
return this.getRealField(data).toRepresent(data);
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return [DependFromFkFieldMixin as any];
}

validateValue(data: RepresentData) {
return this.getRealField(data).validateValue(data);
}

getFieldByFormat(format: Record<string, unknown> | string, data: Record<string, unknown>): Field {
let callback_opt: Record<string, unknown> = {};
if (this.callback) {
callback_opt = this.callback(data);
}
const realField = this.app.fieldsResolver.resolveField(
mergeDeep({ format, callback_opt }),
this.name,
);
if (!realField.model && this.model) {
realField.model = this.model;
}
if (this.app.store.page) {
realField.prepareFieldForView(this.app.store.page.view.path);
}
return realField;
}

getRealField(data: Record<string, unknown>): Field {
_getParentValues(data: Record<string, unknown> = {}): Record<string, unknown> {
const dependFromInstance = data[this.dependField] as Record<string, unknown> | undefined;
const dependFieldValue = (dependFromInstance?.[this.dependFieldAttribute] || 'string') as
| Record<string, unknown>
| string;

return this.getFieldByFormat(dependFieldValue, data);
return { [this.dependFieldAttribute]: dependFieldValue };
}

_getFromValue(data: Record<string, unknown>) {
return this.getFieldByDefinition({ format: data[this.dependFieldAttribute] as string });
}
}
10 changes: 5 additions & 5 deletions frontend_src/vstutils/fields/dynamic/DynamicField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ import type { FieldDefinition } from '@/vstutils/fields/FieldsResolver';
import type { PageView, BaseView } from '@/vstutils/views';
import type { InnerData, RepresentData } from '@/vstutils/utils';

interface DynamicFieldXOptions extends FieldXOptions {
export interface DynamicFieldXOptions extends FieldXOptions {
source_view?: string;
field?: string | string[];
types?: Record<string, FieldDefinition>;
choices?: Record<string, unknown[]>;
callback?: (data: Record<string, unknown>) => FieldDefinition | undefined;
}

export class DynamicField
extends BaseField<unknown, unknown, DynamicFieldXOptions>
implements Field<unknown, unknown, DynamicFieldXOptions>
export class DynamicField<XOptions extends DynamicFieldXOptions = DynamicFieldXOptions>
extends BaseField<unknown, unknown, XOptions>
implements Field<unknown, unknown, XOptions>
{
types: Record<string, Field> | null = null;
usedOnViews = new Set<string>();
sourceView?: PageView | number;

constructor(options: FieldOptions<DynamicFieldXOptions, unknown>) {
constructor(options: FieldOptions<XOptions, unknown>) {
super(options);

onAppAfterInit(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ const schema = createSchema({
'x-options': {
field: 'key',
field_attribute: 'field_type',
types: {
complex_field: { type: 'boolean' },
},
},
},
},
Expand Down Expand Up @@ -217,7 +220,7 @@ describe('DependFromFkField', () => {
results: [
{
id: 1,
field_type: 'boolean',
field_type: 'complex_field',
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion requirements-rpc.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Packages needed for delayed jobs.
celery[redis]==5.3.5
celery[redis]==5.3.6
django-celery-beat~=2.5.0
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ django-environ~=0.11.2
# REST API packages
djangorestframework~=3.14.0
drf-yasg==1.21.7
django-filter==23.3
django-filter==23.4
drf_orjson_renderer==1.7.1
ormsgpack~=1.4.1
pyyaml~=6.0.1
Expand Down
2 changes: 1 addition & 1 deletion test_src/test_proj/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1929,7 +1929,7 @@ def test_openapi_schema_content(self):
self.assertEqual(api['definitions']['Variable']['properties']['value']['format'], 'dynamic_fk')
self.assertEqual(
api['definitions']['Variable']['properties']['value'][X_OPTIONS],
{"field": 'key', 'field_attribute': 'val_type'}
{"field": 'key', 'field_attribute': 'val_type', 'types': {}}
)

# Check that's schema is correct and fields are working
Expand Down
2 changes: 1 addition & 1 deletion vstutils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# pylint: disable=django-not-available
__version__: str = '5.8.16'
__version__: str = '5.8.17'
4 changes: 2 additions & 2 deletions vstutils/api/doc_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ class _YamlOrderedLoader(yaml.SafeLoader):
pass


_YamlOrderedLoader.add_constructor( # type: ignore
_YamlOrderedLoader.add_constructor(
yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG,
lambda loader, node: collections.OrderedDict(loader.construct_pairs(node)) # type: ignore
lambda loader, node: collections.OrderedDict(loader.construct_pairs(node))
)


Expand Down
8 changes: 5 additions & 3 deletions vstutils/api/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ class DynamicJsonTypeField(VSTCharField):
:type field: str
:param types: key-value mapping where key is value of subscribed field and
value is type (in OpenAPI format) of current field.
:type type: dict
:type types: dict
:param choices: variants of choices for different subscribed field values.
Uses mapping where key is value of subscribed field and
value is list with values to choice.
Expand Down Expand Up @@ -371,6 +371,9 @@ class DependFromFkField(DynamicJsonTypeField):
:type field: str
:param field_attribute: attribute of related model instance with name of type.
:type field_attribute: str
:param types: key-value mapping where key is value of subscribed field and
value is type (in OpenAPI format) of current field.
:type types: dict
.. warning::
``field_attribute`` in related model must be :class:`rest_framework.fields.ChoicesField` or
Expand All @@ -381,9 +384,8 @@ class DependFromFkField(DynamicJsonTypeField):
default_related_field = VSTCharField(allow_null=True, allow_blank=True, default='')

def __init__(self, **kwargs):
self.field = kwargs.pop('field')
self.field_attribute = kwargs.pop('field_attribute')
super(DynamicJsonTypeField, self).__init__(**kwargs) # pylint: disable=bad-super-call
super().__init__(**kwargs) # pylint: disable=bad-super-call

def get_value(self, dictionary: _t.Any) -> _t.Any:
value = super().get_value(dictionary)
Expand Down
2 changes: 1 addition & 1 deletion vstutils/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def get_etag_value(cls, pk=None):
hashable_str = '_'.join(c for c, _ in settings.LANGUAGES) + (f'_{pk}' if pk is not None else '')
if settings.ENABLE_CUSTOM_TRANSLATIONS:
hashable_str += CustomTranslations.get_etag_value(pk)
return hashlib.md5(hashable_str.encode('utf-8')).hexdigest()
return hashlib.md5(hashable_str.encode('utf-8')).hexdigest() # nosec

def _get_translation_data(self, module_path_string, code, for_server=False):
data = {}
Expand Down
2 changes: 1 addition & 1 deletion vstutils/api/schema/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import django
from django.conf import settings
from django.urls import reverse_lazy
from rest_framework import __version__ as drf_version # type: ignore
from rest_framework import __version__ as drf_version
from fastapi import __version__ as fastapi_version
from drf_yasg import openapi, __version__ as drf_yasg_version # type: ignore
try:
Expand Down
12 changes: 6 additions & 6 deletions vstutils/api/schema/inspectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,17 +201,17 @@ def field_to_swagger_object(self, field, swagger_object_type, use_references, **
if isinstance(field, fields.DependFromFkField):
field_format = FORMAT_DYN_FK
options['field_attribute'] = field.field_attribute

else:
field_format = FORMAT_DYN
if field.source_view:
options['source_view'] = field.source_view
options['choices'] = field.choices
options['types'] = {}
for name, field_type in field.types.items():
if isinstance(field_type, Field):
field_type = self.probe_field_inspectors(field_type, swagger_object_type, False)
options['types'][name] = field_type

options['types'] = {}
for name, field_type in field.types.items():
if isinstance(field_type, Field):
field_type = self.probe_field_inspectors(field_type, swagger_object_type, False)
options['types'][name] = field_type

kwargs = {
'type': openapi.TYPE_STRING,
Expand Down
2 changes: 1 addition & 1 deletion vstutils/templatetags/vst_gravatar.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ def get_user_gravatar(user_id):
if not user.email:
return static('img/anonymous.png')
url_base = 'https://www.gravatar.com/avatar/{}?d=mp'
user_hash = hashlib.md5(user.email.lower().encode('utf-8')).hexdigest()
user_hash = hashlib.md5(user.email.lower().encode('utf-8')).hexdigest() # nosec
return url_base.format(user_hash)
2 changes: 1 addition & 1 deletion vstutils/utils.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ class StaticFilesHandlers(ObjectHandlers):


class ModelHandlers(ObjectHandlers):
def get_object(self, name: tp.Text, obj) -> tp.Any: # type: ignore[override]
def get_object(self, name: tp.Text, obj) -> tp.Any:
...


Expand Down

0 comments on commit c860660

Please sign in to comment.