Skip to content
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
17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"test": "yarn && yarn workspaces run test",
"test-migration": "yarn && yarn workspaces run test-migration",
"postinstall": "yarn workspace @joystream/types build && yarn workspace storage-node run build",
"postinstall": "yarn workspace @joystream/types build",
"cargo-checks": "devops/git-hooks/pre-commit && devops/git-hooks/pre-push",
"cargo-build": "scripts/cargo-build.sh",
"lint": "yarn workspaces run lint"
Expand All @@ -23,14 +23,15 @@
"devops/prettier-config"
],
"resolutions": {
"@polkadot/api": "^0.96.1",
"@polkadot/api-contract": "^0.96.1",
"@polkadot/keyring": "^1.7.0-beta.5",
"@polkadot/types": "^0.96.1",
"@polkadot/util": "^1.7.0-beta.5",
"@polkadot/util-crypto": "^1.7.0-beta.5",
"@polkadot/api": "^1.26.1",
"@polkadot/api-contract": "^1.26.1",
"@polkadot/keyring": "^3.0.1",
"@polkadot/types": "^1.26.1",
"@polkadot/util": "^3.0.1",
"@polkadot/util-crypto": "^3.0.1",
"@polkadot/wasm-crypto": "^1.2.1",
"babel-core": "^7.0.0-bridge.0",
"typescript": "^3.7.2"
"typescript": "^3.9.7"
},
"devDependencies": {
"husky": "^4.2.5",
Expand Down
1 change: 1 addition & 0 deletions types/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
hiring/schemas/role.schema.json
lib/
build/
src/definitions
12 changes: 9 additions & 3 deletions types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,26 @@
"build": "tsc --build tsconfig.json",
"lint": "eslint ./ --quiet --ext .ts",
"format": "prettier ./ --write",
"checks": "yarn build && madge --circular ./ && yarn lint && prettier ./ --check"
"checks": "yarn build && madge --circular ./ && yarn lint && prettier ./ --check",
"generate:defs": "ts-node node_modules/.bin/polkadot-types-from-defs --package ./src --input ./src/definitions",
"update:augment-types": "tsc --build tsconfig-scripts.json && node ./src/scripts/updateAugmentTypes.js",
"print:typedef": "tsc --build tsconfig-scripts.json && node ./src/scripts/defsFromTypes.js"
},
"author": "Joystream contributors",
"maintainers": [],
"dependencies": {
"@polkadot/types": "^0.96.1",
"@polkadot/keyring": "^1.7.0-beta.5",
"@polkadot/api": "^1.26.1",
"@polkadot/types": "^1.26.1",
"@polkadot/keyring": "^3.0.1",
"@types/lodash": "^4.14.157",
"@types/vfile": "^4.0.0",
"ajv": "^6.11.0",
"lodash": "^4.17.15",
"moment": "^2.24.0"
},
"devDependencies": {
"@polkadot/typegen": "^1.26.1",
"ts-node": "^8.6.2",
"typescript": "^3.7.2"
},
"engines": {
Expand Down
36 changes: 22 additions & 14 deletions types/src/JoyEnum.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
import { Constructor } from '@polkadot/types/types'
import { Constructor, Registry } from '@polkadot/types/types'
import { Enum } from '@polkadot/types/codec'
import { EnumConstructor } from '@polkadot/types/codec/Enum'

export interface ExtendedEnum<Types extends Record<string, Constructor>> extends Enum {
isOfType: (type: keyof Types) => boolean
asType<TypeKey extends keyof Types>(type: TypeKey): InstanceType<Types[TypeKey]>
typeDefinitions: Types
type: keyof Types & string // More typesafe type for the original Enum property
}

export interface ExtendedEnumConstructor<Types extends Record<string, Constructor>>
extends EnumConstructor<ExtendedEnum<Types>> {
create<TypeKey extends keyof Types>(typeKey: TypeKey, value: InstanceType<Types[TypeKey]>): ExtendedEnum<Types>
create<TypeKey extends keyof Types>(
registry: Registry,
typeKey: TypeKey,
value: InstanceType<Types[TypeKey]>
): ExtendedEnum<Types>
typeDefinitions: Types
}

// Helper for creating extended Enum type with TS-compatible isOfType and asType helpers
export function JoyEnum<Types extends Record<string, Constructor>>(types: Types): ExtendedEnumConstructor<Types> {
// Unique values check
if (Object.values(types).some((val, i) => Object.values(types).indexOf(val, i + 1) !== -1)) {
throw new Error('Values passed to JoyEnum are not unique. Create an individual class for each value.')
}

return class JoyEnumObject extends Enum {
public static create<TypeKey extends keyof Types>(typeKey: TypeKey, value: InstanceType<Types[TypeKey]>) {
return new JoyEnumObject({ [typeKey]: value })
return class JoyEnumObject extends Enum.with(types) {
static typeDefinitions = types
typeDefinitions = JoyEnumObject.typeDefinitions // Non-static version
public static create<TypeKey extends keyof Types>(
registry: Registry,
typeKey: TypeKey,
value: InstanceType<Types[TypeKey]>
) {
return new JoyEnumObject(registry, { [typeKey]: value })
}
constructor(value?: any, index?: number) {
super(types, value, index)
constructor(registry: Registry, value?: any, index?: number) {
super(registry, value, index)
}
public isOfType(typeKey: keyof Types) {
return this.value instanceof types[typeKey]
return this.type === typeKey
}
public asType<TypeKey extends keyof Types>(typeKey: TypeKey) {
if (!(this.value instanceof types[typeKey])) {
if (!this.isOfType(typeKey)) {
throw new Error(`Enum.asType(${typeKey}) - value is not of type ${typeKey}`)
}
return this.value as InstanceType<Types[TypeKey]>
Expand Down
97 changes: 63 additions & 34 deletions types/src/JoyStruct.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,73 @@
import { Option, Struct, Enum } from '@polkadot/types/codec'
import { Text, bool as Bool } from '@polkadot/types'
import { Codec } from '@polkadot/types/types'
import { Struct } from '@polkadot/types/codec'
import { Codec, Constructor, Registry } from '@polkadot/types/types'

export class JoyStruct<
T extends {
[K: string]: Codec
}
> extends Struct {
getField<C extends Codec>(name: keyof T): C {
return super.get(name as string) as C
}

getString(name: keyof T): string {
return this.getField<Text>(name).toString()
}
export interface ExtendedStruct<FieldTypes extends Record<string, Constructor>> extends Struct<FieldTypes> {
getField<FieldKey extends keyof FieldTypes>(key: FieldKey): InstanceType<FieldTypes[FieldKey]>
getString<FieldKey extends keyof FieldTypes>(key: FieldKey): string
cloneValues(): { [k in keyof FieldTypes]: FieldTypes[k] }
}

getBoolean(name: keyof T): boolean {
return this.getField<Bool>(name).valueOf()
}
// Those getters are automatically added via Object.defineProperty when using Struct.with
export type ExtendedStructGetters<FieldTypes extends Record<string, Constructor>> = {
[k in keyof FieldTypes]: InstanceType<FieldTypes[k]>
}
// More rich TypeScript definition of the Struct (includes automatically created getters)
export type ExtendedStructDecorated<FieldTypes extends Record<string, Constructor>> = ExtendedStructGetters<
FieldTypes
> &
ExtendedStruct<FieldTypes>

getEnumAsString<EnumValue extends string>(name: keyof T): EnumValue {
return this.getField<Enum>(name).toString() as EnumValue
}
export interface StructConstructor<
FieldTypes extends Record<string, Constructor>,
StructType extends Struct<FieldTypes>
> {
new (registry: Registry, value?: { [k in keyof FieldTypes]: InstanceType<FieldTypes[k]> }): StructType
}

unwrapOrUndefined<C extends Codec>(name: keyof T): C | undefined {
return this.getField<Option<C>>(name).unwrapOr(undefined)
}
export type ExtendedStructConstructor<FieldTypes extends Record<string, Constructor>> = StructConstructor<
FieldTypes,
ExtendedStruct<FieldTypes>
>

getOptionalString(name: keyof T): string | undefined {
const text = this.unwrapOrUndefined<Text>(name)
return text ? text.toString() : undefined
}
export type ExtendedStructDecoratedConstructor<FieldTypes extends Record<string, Constructor>> = StructConstructor<
FieldTypes,
ExtendedStructDecorated<FieldTypes>
>

cloneValues(): T {
const objectClone = {} as { [K: string]: Codec }
// Helper for creating extended Struct type with TS-compatible interface
// It's called JoyStructCustom, because eventually we'd want to migrate all structs to JoyStructDecorated,
// but the latter won't allow specifying getters that return different type than the original field type.
// (ie. by using getString() instead of getField())
export function JoyStructCustom<FieldTypes extends Record<string, Constructor>>(
fields: FieldTypes
): ExtendedStructConstructor<FieldTypes> {
return class JoyStructObject extends Struct.with(fields) {
constructor(registry: Registry, value?: { [k in keyof FieldTypes]: InstanceType<FieldTypes[k]> }) {
super(registry, value)
}
getField<FieldKey extends keyof FieldTypes>(key: FieldKey): InstanceType<FieldTypes[FieldKey]> {
return this.get(key as string) as InstanceType<FieldTypes[FieldKey]>
}
getString<FieldKey extends keyof FieldTypes>(key: FieldKey): string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest getFieldAsString as a better name

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not worth spending more time on this however now, there is more important work.

return this.getField(key).toString()
}
// TODO: Check why would this ever be needed
cloneValues(): { [k in keyof FieldTypes]: FieldTypes[k] } {
const objectClone = {} as Partial<{ [k in keyof FieldTypes]: Codec }>

super.forEach((v, k) => {
objectClone[k] = v // shallow copy acceptable ?
})
super.forEach((v, k) => {
objectClone[k] = v // shallow copy acceptable ?
})

return objectClone as T
return (objectClone as unknown) as { [k in keyof FieldTypes]: FieldTypes[k] }
}
}
}

// JoyStruct enriched with typescript definitions for getters automatically added by polkadot-js
export function JoyStructDecorated<FieldTypes extends Record<string, Constructor>>(
fields: FieldTypes
): ExtendedStructDecoratedConstructor<FieldTypes> {
// We need to cast here because there's no way to make TS aware of getters added with Object.defineProperty
return JoyStructCustom(fields) as ExtendedStructDecoratedConstructor<FieldTypes>
}
98 changes: 19 additions & 79 deletions types/src/common.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { Struct, Option, Text, bool, Vec, u16, u32, u64, getTypeRegistry, Null } from '@polkadot/types'
import { Struct, Option, Text, bool, Vec, u16, u32, u64, Null } from '@polkadot/types'
import { BlockNumber, Moment } from '@polkadot/types/interfaces'
import { Codec } from '@polkadot/types/types'
import { Codec, RegistryTypes } from '@polkadot/types/types'
// we get 'moment' because it is a dependency of @polkadot/util, via @polkadot/keyring
import moment from 'moment'
import { JoyStruct } from './JoyStruct'
import { JoyStructCustom, JoyStructDecorated } from './JoyStruct'
import { JoyEnum } from './JoyEnum'
export { JoyStruct } from './JoyStruct'
export { JoyEnum } from './JoyEnum'

// Treat a BTreeSet as a Vec since it is encoded in the same way
export class BTreeSet<T extends Codec> extends Vec<T> {}
export { JoyEnum, JoyStructCustom, JoyStructDecorated }

export class Credential extends u64 {}
export class CredentialSet extends Vec.with(Credential) {} // BtreeSet ?
Expand All @@ -23,29 +20,7 @@ export type BlockAndTimeType = {
time: Moment
}

export class BlockAndTime extends Struct {
constructor(value?: BlockAndTimeType) {
super(
{
block: u32, // BlockNumber
time: u64, // Moment
},
value
)
}

get block(): BlockNumber {
return this.get('block') as BlockNumber
}

get time(): Moment {
return this.get('time') as Moment
}

static newEmpty(): BlockAndTime {
return new BlockAndTime({} as BlockAndTime)
}

export class BlockAndTime extends JoyStructDecorated({ block: u32, time: u64 }) implements BlockAndTimeType {
get momentDate(): moment.Moment {
const YEAR_2000_MILLISECONDS = 946684801000

Expand Down Expand Up @@ -76,49 +51,17 @@ export function getOptionPropOrUndefined<T extends Codec>(struct: Struct, fieldN
return (struct.get(fieldName) as Option<T>).unwrapOr(undefined)
}

export class OptionText extends Option.with(Text) {
static none(): OptionText {
return new Option(Text, null)
}

static some(text: string): OptionText {
return new Option(Text, text)
}
}
export class OptionText extends Option.with(Text) {}

export type InputValidationLengthConstraintType = {
min: u16
max_min_diff: u16
}

export class InputValidationLengthConstraint extends JoyStruct<InputValidationLengthConstraintType> {
constructor(value: InputValidationLengthConstraintType) {
super(
{
min: u16,
max_min_diff: u16,
},
value
)
}

static createWithMaxAllowed() {
return new InputValidationLengthConstraint({
min: new u16(1),
max_min_diff: new u16(65534), // Max allowed without causing u16 overflow
})
}

get min(): u16 {
return this.getField('min')
}

get max_min_diff(): u16 {
return this.getField('max_min_diff')
}

export class InputValidationLengthConstraint extends JoyStructDecorated({ min: u16, max_min_diff: u16 })
implements InputValidationLengthConstraintType {
get max(): u16 {
return new u16(this.min.add(this.max_min_diff))
return new u16(this.registry, this.min.add(this.max_min_diff))
}
}

Expand All @@ -128,17 +71,14 @@ export const WorkingGroupDef = {
export type WorkingGroupKey = keyof typeof WorkingGroupDef
export class WorkingGroup extends JoyEnum(WorkingGroupDef) {}

export function registerCommonTypes() {
const typeRegistry = getTypeRegistry()

typeRegistry.register({
Credential,
CredentialSet,
BlockAndTime,
ThreadId,
PostId,
InputValidationLengthConstraint,
BTreeSet, // Is this even necessary?
WorkingGroup,
})
export const commonTypes: RegistryTypes = {
Credential,
CredentialSet,
BlockAndTime,
ThreadId,
PostId,
InputValidationLengthConstraint,
WorkingGroup,
}

export default commonTypes
Loading