Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F3010145
collection-item-body.ts
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
collection-item-body.ts
View Options
import
type
Collection
from
"./collection"
;
import
type
{
Context
,
ExtractInput
}
from
"../main"
;
import
type
{
FieldOutput
,
RequiredField
}
from
"./field"
;
export
type
FieldNames
<
T
extends
Collection
>
=
keyof
T
[
"fields"
]
&
string
;
export
type
RequiredItemFields
<
T
extends
Collection
>
=
{
[
FieldName
in
FieldNames
<
T
>
as
T
[
"fields"
][
FieldName
]
extends
RequiredField
?
FieldName
:
never
]
:
ExtractInput
<
T
[
"fields"
][
FieldName
]
>
;
};
export
type
NonrequiredItemFields
<
T
extends
Collection
>
=
{
[
FieldName
in
FieldNames
<
T
>
as
T
[
"fields"
][
FieldName
]
extends
RequiredField
?
never
:
FieldName
]
:
ExtractInput
<
T
[
"fields"
][
FieldName
]
>
;
};
export
type
ItemFields
<
T
extends
Collection
>
=
RequiredItemFields
<
T
>
&
Partial
<
NonrequiredItemFields
<
T
>>
;
export
type
ItemFieldsOutput
<
T
extends
Collection
>
=
{
[
field
in
FieldNames
<
T
>
]
:
FieldOutput
<
T
[
"fields"
][
field
]
>
;
};
export
default
class
CollectionItemBody
<
T
extends
Collection
=
any
>
{
changed_fields
:
Set
<
keyof
ItemFields
<
T
>>
=
new
Set
();
is_decoded
=
false
;
is_encoded
=
false
;
blessings
:
Partial
<
Record
<
keyof
ItemFields
<
T
>
,
symbol
|
null
>>
=
{};
constructor
(
public
collection
:
T
,
public
raw_input
:
Partial
<
ItemFields
<
T
>>
=
{},
public
decoded
:
Partial
<
ItemFields
<
T
>>
=
{},
public
encoded
:
Partial
<
ItemFields
<
T
>>
=
{}
)
{
for
(
const
field_name
in
raw_input
)
{
if
(
!
encoded
[
field_name
as
FieldNames
<
T
>
])
{
this
.
changed_fields
.
add
(
field_name
as
FieldNames
<
T
>
);
}
}
}
set
<
FieldName
extends
keyof
ItemFields
<
T
>>
(
field_name
:
FieldName
,
field_value
:
ItemFields
<
T
>
[
FieldName
],
blessing_symbol
:
symbol
|
null
=
null
// those symbols can be used as a proof that a value came from e.g. an internal callback, and not from user input
)
:
this
{
this
.
raw_input
[
field_name
]
=
field_value
;
this
.
is_decoded
=
false
;
this
.
is_encoded
=
false
;
this
.
changed_fields
.
add
(
field_name
);
this
.
blessings
[
field_name
]
=
blessing_symbol
;
return
this
;
}
clearChangedFields
()
{
this
.
changed_fields
.
clear
();
}
getDecoded
<
FieldName
extends
keyof
ItemFields
<
T
>>
(
field_name
:
FieldName
)
{
if
(
!
this
.
is_decoded
)
{
throw
new
Error
(
"Decode first!"
);
}
return
this
.
decoded
[
field_name
];
}
getInput
<
FieldName
extends
keyof
ItemFields
<
T
>>
(
field_name
:
FieldName
)
{
return
this
.
raw_input
[
field_name
];
}
getEncoded
<
FieldName
extends
keyof
ItemFields
<
T
>>
(
field_name
:
FieldName
)
{
return
this
.
encoded
[
field_name
];
}
/** Returns encoded values for every field */
async
encode
(
context
:
Context
,
only_changed
=
false
)
:
Promise
<
ItemFields
<
T
>>
{
context
.
app
.
Logger
.
debug3
(
"ITEM BODY"
,
"encode"
,
this
.
changed_fields
.
values
()
);
const
new_encoded
:
Partial
<
ItemFields
<
T
>>
=
{};
const
promises
=
[];
for
(
const
field_name
of
this
.
changed_fields
.
values
())
{
const
to_encode
=
this
.
raw_input
[
field_name
];
context
.
app
.
Logger
.
debug3
(
"ITEM BODY"
,
"encoding value"
,
{
[
field_name
]
:
this
.
raw_input
[
field_name
],
is_the_value_empty
:
to_encode
===
undefined
||
to_encode
===
null
,
});
if
(
!
this
.
collection
.
fields
[
field_name
as
string
])
{
// field does not exist in this collection
continue
;
}
if
(
to_encode
===
undefined
)
{
new_encoded
[
field_name
as
FieldNames
<
T
>
]
=
null
;
continue
;
}
promises
.
push
(
this
.
collection
.
fields
[
field_name
as
string
]
.
encode
(
context
,
to_encode
)
.
then
((
value
)
=>
{
new_encoded
[
field_name
as
FieldNames
<
T
>
]
=
value
;
})
);
}
await
Promise
.
all
(
promises
);
this
.
encoded
=
{
...
this
.
encoded
,
...
new_encoded
};
this
.
is_encoded
=
true
;
context
.
app
.
Logger
.
debug2
(
"ITEM BODY"
,
"encode result"
,
this
.
encoded
);
return
only_changed
?
(
new_encoded
as
ItemFields
<
T
>
)
:
(
this
.
encoded
as
ItemFields
<
T
>
);
}
async
decode
(
context
:
Context
,
format
:
{
[
field_name
:
string
]
:
any
}
=
{}
)
:
Promise
<
CollectionItemBody
<
T
>>
{
if
(
this
.
is_decoded
)
return
this
;
context
.
app
.
Logger
.
debug3
(
"ITEM BODY"
,
"Decoding item"
,
{
format
,
body
:
this
.
encoded
,
});
const
promises
:
Promise
<
any
>
[]
=
[];
for
(
const
field_name
in
this
.
encoded
)
{
if
(
!
this
.
collection
.
fields
?
.[
field_name
])
{
continue
;
}
promises
.
push
(
this
.
collection
.
fields
?
.[
field_name
]
.
decode
(
context
,
this
.
encoded
[
field_name
as
FieldNames
<
T
>
],
null
,
format
?
.[
field_name
]
)
.
then
((
decoded_value
)
=>
{
this
.
decoded
=
{
...
this
.
decoded
,
[
field_name
]
:
decoded_value
,
};
context
.
app
.
Logger
.
debug3
(
"ITEM BODY"
,
"Decoded value"
,
{
[
field_name
]
:
decoded_value
,
}
);
})
);
}
await
Promise
.
all
(
promises
);
this
.
is_decoded
=
true
;
return
this
;
}
static
fromDB
(
collection
:
Collection
,
database_entry
:
{
[
field_name
:
string
]
:
any
}
)
{
delete
database_entry
.
_id
;
//the mongo ID
return
new
CollectionItemBody
<
typeof
collection
>
(
collection
,
{},
{},
database_entry
);
}
static
fromDecoded
<
T
extends
Collection
>
(
collection
:
T
,
decoded
:
Partial
<
ItemFields
<
T
>>
)
{
return
new
CollectionItemBody
<
T
>
(
collection
,
{},
decoded
,
{});
}
copy
()
:
CollectionItemBody
<
T
>
{
return
new
CollectionItemBody
<
T
>
(
this
.
collection
,
{
...
this
.
raw_input
},
{
...
this
.
decoded
},
{
...
this
.
encoded
}
);
}
async
validate
(
context
:
Context
,
original_body
:
CollectionItemBody
,
replace_mode
:
boolean
//if true, meaning that if a field has no value, it should be deleted
)
:
Promise
<
{
valid
:
boolean
;
errors
:
{
[
f
in
keyof
ItemFields
<
T
>
]
?:
{
message
:
string
}
};
}
>
{
const
promises
=
[];
const
errors
:
{
[
f
in
keyof
ItemFields
<
T
>
]
?:
{
message
:
string
}
}
=
{};
let
valid
=
true
;
const
fields_to_check
=
new
Set
(
this
.
changed_fields
.
values
());
if
(
replace_mode
)
{
for
(
const
field
of
this
.
collection
.
getRequiredFields
())
{
fields_to_check
.
add
(
field
.
name
as
FieldNames
<
T
>
);
}
}
for
(
const
field_name
of
fields_to_check
)
{
if
(
!
this
.
collection
.
fields
[
field_name
as
string
])
{
// field does not exist
continue
;
}
promises
.
push
(
this
.
collection
.
fields
[
field_name
as
string
]
.
checkValue
(
context
,
this
.
raw_input
[
field_name
],
original_body
.
encoded
[
field_name
as
FieldNames
<
T
>
],
this
.
blessings
[
field_name
]
||
null
)
.
then
(
async
(
result
)
=>
{
if
(
!
result
.
valid
)
{
valid
=
false
;
errors
[
field_name
]
=
{
message
:
result
.
reason
as
string
,
};
}
})
);
}
await
Promise
.
all
(
promises
);
return
{
valid
,
errors
};
}
static
empty
<
T
extends
Collection
>
(
collection
:
T
)
:
CollectionItemBody
<
T
>
{
return
new
CollectionItemBody
<
T
>
(
collection
,
{},
{},
{});
}
getBlessing
<
FieldName
extends
keyof
ItemFields
<
T
>>
(
field_name
:
FieldName
)
:
symbol
|
null
{
return
this
.
blessings
[
field_name
]
||
null
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-java
Expires
Wed, May 7, 19:37 (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
662591
Default Alt Text
collection-item-body.ts (6 KB)
Attached To
Mode
rS Sealious
Attached
Detach File
Event Timeline
Log In to Comment