forked from kubernetes-client/javascript
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request kubernetes-client#1695 from schrodit/fix-obj-typing
Properly parse metadata of custom Kubernetes objects
- Loading branch information
1 parent
b8705f0
commit b85a634
Showing
6 changed files
with
279 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { ObjectSerializer as InternalSerializer, V1ObjectMeta } from './gen/models/ObjectSerializer'; | ||
|
||
type AttributeType = { | ||
name: string; | ||
baseName: string; | ||
type: string; | ||
format: string; | ||
}; | ||
|
||
class KubernetesObject { | ||
/** | ||
* APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | ||
*/ | ||
'apiVersion'?: string; | ||
/** | ||
* Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | ||
*/ | ||
'kind'?: string; | ||
'metadata'?: V1ObjectMeta; | ||
|
||
static attributeTypeMap: AttributeType[] = [ | ||
{ | ||
name: 'apiVersion', | ||
baseName: 'apiVersion', | ||
type: 'string', | ||
format: '', | ||
}, | ||
{ | ||
name: 'kind', | ||
baseName: 'kind', | ||
type: 'string', | ||
format: '', | ||
}, | ||
{ | ||
name: 'metadata', | ||
baseName: 'metadata', | ||
type: 'V1ObjectMeta', | ||
format: '', | ||
}, | ||
]; | ||
} | ||
|
||
const isKubernetesObject = (data: unknown): boolean => | ||
!!data && typeof data === 'object' && 'apiVersion' in data && 'kind' in data; | ||
|
||
/** | ||
* Wraps the ObjectSerializer to support custom resources and generic Kubernetes objects. | ||
*/ | ||
export class ObjectSerializer extends InternalSerializer { | ||
public static serialize(data: any, type: string, format: string = ''): any { | ||
const obj = InternalSerializer.serialize(data, type, format); | ||
if (obj !== data) { | ||
return obj; | ||
} | ||
|
||
if (!isKubernetesObject(data)) { | ||
return obj; | ||
} | ||
|
||
const instance: Record<string, any> = {}; | ||
for (const attributeType of KubernetesObject.attributeTypeMap) { | ||
const value = data[attributeType.baseName]; | ||
if (value !== undefined) { | ||
instance[attributeType.name] = InternalSerializer.serialize( | ||
data[attributeType.baseName], | ||
attributeType.type, | ||
attributeType.format, | ||
); | ||
} | ||
} | ||
// add all unknown properties as is. | ||
for (const [key, value] of Object.entries(data)) { | ||
if (KubernetesObject.attributeTypeMap.find((t) => t.name === key)) { | ||
continue; | ||
} | ||
instance[key] = value; | ||
} | ||
return instance; | ||
} | ||
|
||
public static deserialize(data: any, type: string, format: string = ''): any { | ||
const obj = InternalSerializer.deserialize(data, type, format); | ||
if (obj !== data) { | ||
// the serializer knows the type and already deserialized it. | ||
return obj; | ||
} | ||
|
||
if (!isKubernetesObject(data)) { | ||
return obj; | ||
} | ||
|
||
const instance = new KubernetesObject(); | ||
for (const attributeType of KubernetesObject.attributeTypeMap) { | ||
const value = data[attributeType.baseName]; | ||
if (value !== undefined) { | ||
instance[attributeType.name] = InternalSerializer.deserialize( | ||
data[attributeType.baseName], | ||
attributeType.type, | ||
attributeType.format, | ||
); | ||
} | ||
} | ||
// add all unknown properties as is. | ||
for (const [key, value] of Object.entries(data)) { | ||
if (KubernetesObject.attributeTypeMap.find((t) => t.name === key)) { | ||
continue; | ||
} | ||
instance[key] = value; | ||
} | ||
return instance; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import { expect } from 'chai'; | ||
import { ObjectSerializer } from './serializer'; | ||
|
||
describe('ObjectSerializer', () => { | ||
describe('serialize', () => { | ||
it('should serialize a known object', () => { | ||
const s = { | ||
apiVersion: 'v1', | ||
kind: 'Secret', | ||
metadata: { | ||
name: 'k8s-js-client-test', | ||
namespace: 'default', | ||
creationTimestamp: new Date('2022-01-01T00:00:00.000Z'), | ||
}, | ||
data: { | ||
key: 'value', | ||
}, | ||
}; | ||
const res = ObjectSerializer.serialize(s, 'V1Secret'); | ||
expect(res).to.deep.equal({ | ||
apiVersion: 'v1', | ||
kind: 'Secret', | ||
metadata: { | ||
name: 'k8s-js-client-test', | ||
namespace: 'default', | ||
creationTimestamp: '2022-01-01T00:00:00.000Z', | ||
uid: undefined, | ||
annotations: undefined, | ||
labels: undefined, | ||
finalizers: undefined, | ||
generateName: undefined, | ||
selfLink: undefined, | ||
resourceVersion: undefined, | ||
generation: undefined, | ||
ownerReferences: undefined, | ||
deletionTimestamp: undefined, | ||
deletionGracePeriodSeconds: undefined, | ||
managedFields: undefined, | ||
}, | ||
data: { | ||
key: 'value', | ||
}, | ||
type: undefined, | ||
immutable: undefined, | ||
stringData: undefined, | ||
}); | ||
}); | ||
|
||
it('should serialize a unknown kubernetes object', () => { | ||
const s = { | ||
apiVersion: 'v1alpha1', | ||
kind: 'MyCustomResource', | ||
metadata: { | ||
name: 'k8s-js-client-test', | ||
namespace: 'default', | ||
creationTimestamp: new Date('2022-01-01T00:00:00.000Z'), | ||
}, | ||
data: { | ||
key: 'value', | ||
}, | ||
}; | ||
const res = ObjectSerializer.serialize(s, 'v1alpha1MyCustomResource'); | ||
expect(res).to.deep.equal({ | ||
apiVersion: 'v1alpha1', | ||
kind: 'MyCustomResource', | ||
metadata: { | ||
name: 'k8s-js-client-test', | ||
namespace: 'default', | ||
creationTimestamp: '2022-01-01T00:00:00.000Z', | ||
uid: undefined, | ||
annotations: undefined, | ||
labels: undefined, | ||
finalizers: undefined, | ||
generateName: undefined, | ||
selfLink: undefined, | ||
resourceVersion: undefined, | ||
generation: undefined, | ||
ownerReferences: undefined, | ||
deletionTimestamp: undefined, | ||
deletionGracePeriodSeconds: undefined, | ||
managedFields: undefined, | ||
}, | ||
data: { | ||
key: 'value', | ||
}, | ||
}); | ||
}); | ||
|
||
it('should serialize a unknown primitive', () => { | ||
const s = { | ||
key: 'value', | ||
}; | ||
const res = ObjectSerializer.serialize(s, 'unknown'); | ||
expect(res).to.deep.equal(s); | ||
}); | ||
}); | ||
|
||
describe('deserialize', () => { | ||
it('should deserialize a known object', () => { | ||
const s = { | ||
apiVersion: 'v1', | ||
kind: 'Secret', | ||
metadata: { | ||
name: 'k8s-js-client-test', | ||
namespace: 'default', | ||
creationTimestamp: '2022-01-01T00:00:00.000Z', | ||
}, | ||
data: { | ||
key: 'value', | ||
}, | ||
}; | ||
const res = ObjectSerializer.deserialize(s, 'V1Secret'); | ||
expect(res).to.deep.equal({ | ||
apiVersion: 'v1', | ||
kind: 'Secret', | ||
metadata: { | ||
name: 'k8s-js-client-test', | ||
namespace: 'default', | ||
creationTimestamp: new Date('2022-01-01T00:00:00.000Z'), | ||
}, | ||
data: { | ||
key: 'value', | ||
}, | ||
}); | ||
}); | ||
|
||
it('should deserialize a unknown object', () => { | ||
const s = { | ||
apiVersion: 'v1alpha1', | ||
kind: 'MyCustomResource', | ||
metadata: { | ||
name: 'k8s-js-client-test', | ||
namespace: 'default', | ||
creationTimestamp: '2022-01-01T00:00:00.000Z', | ||
}, | ||
data: { | ||
key: 'value', | ||
}, | ||
}; | ||
const res = ObjectSerializer.deserialize(s, 'v1alpha1MyCustomResource'); | ||
expect(res).to.deep.equal({ | ||
apiVersion: 'v1alpha1', | ||
kind: 'MyCustomResource', | ||
metadata: { | ||
name: 'k8s-js-client-test', | ||
namespace: 'default', | ||
creationTimestamp: new Date('2022-01-01T00:00:00.000Z'), | ||
}, | ||
data: { | ||
key: 'value', | ||
}, | ||
}); | ||
}); | ||
|
||
it('should deserialize a unknown primitive', () => { | ||
const s = { | ||
key: 'value', | ||
}; | ||
const res = ObjectSerializer.serialize(s, 'unknown'); | ||
expect(res).to.deep.equal(s); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters