-
Notifications
You must be signed in to change notification settings - Fork 538
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 #1700 from schrodit/cherry-pick-1695
Cherry-Pick #1695
- Loading branch information
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