Skip to content

Commit 7fa1685

Browse files
authored
Mark schema objects with default values as non-nullable (#613)
Partially fixes #584
1 parent 51b3747 commit 7fa1685

23 files changed

+425
-418
lines changed

src/transform/schema.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@ interface TransformSchemaObjOptions extends GlobalContext {
1515
required: Set<string>;
1616
}
1717

18+
function hasDefaultValue(node: any): boolean {
19+
if (node.hasOwnProperty("default")) return true;
20+
// if (node.hasOwnProperty("$ref")) return true; // TODO: resolve remote $refs?
21+
return false;
22+
}
23+
1824
/** Take object keys and convert to TypeScript interface */
1925
export function transformSchemaObjMap(obj: Record<string, any>, options: TransformSchemaObjOptions): string {
20-
const readonly = tsReadonly(options.immutableTypes);
21-
2226
let output = "";
2327

2428
for (const k of Object.keys(obj)) {
@@ -27,7 +31,9 @@ export function transformSchemaObjMap(obj: Record<string, any>, options: Transfo
2731
if (v.description) output += comment(v.description);
2832

2933
// 2. name (with “?” if optional property)
30-
output += `${readonly}"${k}"${options.required.has(k) ? "" : "?"}: `;
34+
const readonly = tsReadonly(options.immutableTypes);
35+
const required = options.required.has(k) || hasDefaultValue(v.schema || v) ? "" : "?";
36+
output += `${readonly}"${k}"${required}: `;
3137

3238
// 3. transform
3339
output += transformSchemaObj(v.schema || v, options);

src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ export interface SchemaObject {
103103
items?: ReferenceObject | SchemaObject;
104104
allOf?: SchemaObject;
105105
properties?: Record<string, ReferenceObject | SchemaObject>;
106+
default?: any;
106107
additionalProperties?: boolean | ReferenceObject | SchemaObject;
107108
nullable?: boolean; // V3 ONLY
108109
oneOf?: (ReferenceObject | SchemaObject)[]; // V3 ONLY

tests/bin/expected/prettier-js.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export interface components {
7272
shipDate?: string
7373
/** Order Status */
7474
status?: 'placed' | 'approved' | 'delivered'
75-
complete?: boolean
75+
complete: boolean
7676
}
7777
Category: {
7878
id?: number

tests/bin/expected/prettier-json.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export interface components {
7272
shipDate?: string
7373
/** Order Status */
7474
status?: 'placed' | 'approved' | 'delivered'
75-
complete?: boolean
75+
complete: boolean
7676
}
7777
Category: {
7878
id?: number

tests/bin/expected/stdout.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export interface components {
7272
shipDate?: string;
7373
/** Order Status */
7474
status?: "placed" | "approved" | "delivered";
75-
complete?: boolean;
75+
complete: boolean;
7676
};
7777
Category: {
7878
id?: number;

tests/v2/expected/http.ts

+19-19
Original file line numberDiff line numberDiff line change
@@ -618,11 +618,11 @@ export interface definitions {
618618
base_url?: string;
619619
sso_url?: string;
620620
version?: "v1";
621-
features?: {
621+
features: {
622622
access_code?: boolean;
623623
sso?: boolean;
624624
plan_change?: boolean;
625-
credential?: "none" | "single" | "multiple" | "unknown";
625+
credential: "none" | "single" | "multiple" | "unknown";
626626
};
627627
};
628628
/** An array of platform ids to restrict this product for. */
@@ -662,24 +662,24 @@ export interface definitions {
662662
name: definitions["Name"];
663663
type: "boolean" | "string" | "number";
664664
/** This sets whether or not the feature can be customized by a consumer. */
665-
customizable?: boolean;
665+
customizable: boolean;
666666
/**
667667
* This sets whether or not the feature can be upgraded by the consumer after the
668668
* resource has provisioned. Upgrading means setting a higher value or selecting a
669669
* higher element in the list.
670670
*/
671-
upgradable?: boolean;
671+
upgradable: boolean;
672672
/**
673673
* This sets whether or not the feature can be downgraded by the consumer after the
674674
* resource has provisioned. Downgrading means setting a lower value or selecting a
675675
* lower element in the list.
676676
*/
677-
downgradable?: boolean;
677+
downgradable: boolean;
678678
/**
679679
* Sets if this feature’s value is trackable from the provider,
680680
* this only really affects numeric constraints.
681681
*/
682-
measurable?: boolean;
682+
measurable: boolean;
683683
values?: definitions["FeatureValuesList"];
684684
};
685685
/**
@@ -699,7 +699,7 @@ export interface definitions {
699699
* is selected or is default for the plan.
700700
* Cost is deprecated in favor of the `price.cost` field.
701701
*/
702-
cost?: number;
702+
cost: number;
703703
/**
704704
* Price describes the cost of a feature. It should be preferred over
705705
* the `cost` property.
@@ -710,13 +710,13 @@ export interface definitions {
710710
* when this value is selected or is default for the plan.
711711
* Number features should use the cost range instead.
712712
*/
713-
cost?: number;
713+
cost: number;
714714
/**
715715
* When a feature is used to multiply the cost of the plan or of
716716
* another feature, multiply factor is used for calculation.
717717
* A feature cannot have both a cost and a multiply factor.
718718
*/
719-
multiply_factor?: number;
719+
multiply_factor: number;
720720
/** Price describes how the feature cost should be calculated. */
721721
formula?: definitions["PriceFormula"];
722722
/** Description explains how a feature is calculated to the user. */
@@ -737,9 +737,9 @@ export interface definitions {
737737
* means this numeric details has no scale, and will not be or customizable.
738738
* Some plans may not have a measureable or customizable feature.
739739
*/
740-
increment?: number;
740+
increment: number;
741741
/** Minimum value that can be set by a user if customizable */
742-
min?: number;
742+
min: number;
743743
/** Maximum value that can be set by a user if customizable */
744744
max?: number;
745745
/** Applied to the end of the number for display, for example the ‘GB’ in ‘20 GB’. */
@@ -758,7 +758,7 @@ export interface definitions {
758758
* An integer in 10,000,000ths of cents, will be multiplied by the
759759
* numeric value set in the feature to determine the cost.
760760
*/
761-
cost_multiple?: number;
761+
cost_multiple: number;
762762
};
763763
FeatureValue: {
764764
feature: definitions["Label"];
@@ -784,40 +784,40 @@ export interface definitions {
784784
* When true, everyone can see the product when requested. When false it will
785785
* not be visible to anyone except those on the provider team.
786786
*/
787-
public?: boolean;
787+
public: boolean;
788788
/**
789789
* When true, the product will be displayed in product listings alongside
790790
* other products. When false the product will be excluded from listings,
791791
* but can still be provisioned directly if it's label is known.
792792
* Any pages that display information about the product when not listed,
793793
* should indicate to webcrawlers that the content should not be indexed.
794794
*/
795-
listed?: boolean;
795+
listed: boolean;
796796
/**
797797
* Object to hold various flags for marketing purposes only. These are values
798798
* that need to be stored, but should not affect decision making in code. If
799799
* we find ourselves in a position where we think they should, we should
800800
* consider refactoring our listing definition.
801801
*/
802-
marketing?: {
802+
marketing: {
803803
/**
804804
* Indicates whether or not the product is in `Beta` and should be
805805
* advertised as such. This does not have any impact on who can access the
806806
* product, it is just used to inform consumers through our clients.
807807
*/
808-
beta?: boolean;
808+
beta: boolean;
809809
/**
810810
* Indicates whether or not the product is in `New` and should be
811811
* advertised as such. This does not have any impact on who can access the
812812
* product, it is just used to inform consumers through our clients.
813813
*/
814-
new?: boolean;
814+
new: boolean;
815815
/**
816816
* Indicates whether or not the product is in `New` and should be
817817
* advertised as such. This does not have any impact on who can access the
818818
* product, it is just used to inform consumers through our clients.
819819
*/
820-
featured?: boolean;
820+
featured: boolean;
821821
};
822822
};
823823
/**
@@ -858,7 +858,7 @@ export interface definitions {
858858
* * `multiple`: Multiple credentials are supported at the same time.
859859
* * `unknown`: The credential type is unknown.
860860
*/
861-
credential?: "none" | "single" | "multiple" | "unknown";
861+
credential: "none" | "single" | "multiple" | "unknown";
862862
};
863863
ProductBody: {
864864
provider_id: definitions["ID"];

tests/v2/expected/manifold.immutable.ts

+19-19
Original file line numberDiff line numberDiff line change
@@ -618,11 +618,11 @@ export interface definitions {
618618
readonly base_url?: string;
619619
readonly sso_url?: string;
620620
readonly version?: "v1";
621-
readonly features?: {
621+
readonly features: {
622622
readonly access_code?: boolean;
623623
readonly sso?: boolean;
624624
readonly plan_change?: boolean;
625-
readonly credential?: "none" | "single" | "multiple" | "unknown";
625+
readonly credential: "none" | "single" | "multiple" | "unknown";
626626
};
627627
};
628628
/** An array of platform ids to restrict this product for. */
@@ -662,24 +662,24 @@ export interface definitions {
662662
readonly name: definitions["Name"];
663663
readonly type: "boolean" | "string" | "number";
664664
/** This sets whether or not the feature can be customized by a consumer. */
665-
readonly customizable?: boolean;
665+
readonly customizable: boolean;
666666
/**
667667
* This sets whether or not the feature can be upgraded by the consumer after the
668668
* resource has provisioned. Upgrading means setting a higher value or selecting a
669669
* higher element in the list.
670670
*/
671-
readonly upgradable?: boolean;
671+
readonly upgradable: boolean;
672672
/**
673673
* This sets whether or not the feature can be downgraded by the consumer after the
674674
* resource has provisioned. Downgrading means setting a lower value or selecting a
675675
* lower element in the list.
676676
*/
677-
readonly downgradable?: boolean;
677+
readonly downgradable: boolean;
678678
/**
679679
* Sets if this feature’s value is trackable from the provider,
680680
* this only really affects numeric constraints.
681681
*/
682-
readonly measurable?: boolean;
682+
readonly measurable: boolean;
683683
readonly values?: definitions["FeatureValuesList"];
684684
};
685685
/**
@@ -699,7 +699,7 @@ export interface definitions {
699699
* is selected or is default for the plan.
700700
* Cost is deprecated in favor of the `price.cost` field.
701701
*/
702-
readonly cost?: number;
702+
readonly cost: number;
703703
/**
704704
* Price describes the cost of a feature. It should be preferred over
705705
* the `cost` property.
@@ -710,13 +710,13 @@ export interface definitions {
710710
* when this value is selected or is default for the plan.
711711
* Number features should use the cost range instead.
712712
*/
713-
readonly cost?: number;
713+
readonly cost: number;
714714
/**
715715
* When a feature is used to multiply the cost of the plan or of
716716
* another feature, multiply factor is used for calculation.
717717
* A feature cannot have both a cost and a multiply factor.
718718
*/
719-
readonly multiply_factor?: number;
719+
readonly multiply_factor: number;
720720
/** Price describes how the feature cost should be calculated. */
721721
readonly formula?: definitions["PriceFormula"];
722722
/** Description explains how a feature is calculated to the user. */
@@ -737,9 +737,9 @@ export interface definitions {
737737
* means this numeric details has no scale, and will not be or customizable.
738738
* Some plans may not have a measureable or customizable feature.
739739
*/
740-
readonly increment?: number;
740+
readonly increment: number;
741741
/** Minimum value that can be set by a user if customizable */
742-
readonly min?: number;
742+
readonly min: number;
743743
/** Maximum value that can be set by a user if customizable */
744744
readonly max?: number;
745745
/** Applied to the end of the number for display, for example the ‘GB’ in ‘20 GB’. */
@@ -758,7 +758,7 @@ export interface definitions {
758758
* An integer in 10,000,000ths of cents, will be multiplied by the
759759
* numeric value set in the feature to determine the cost.
760760
*/
761-
readonly cost_multiple?: number;
761+
readonly cost_multiple: number;
762762
};
763763
readonly FeatureValue: {
764764
readonly feature: definitions["Label"];
@@ -784,40 +784,40 @@ export interface definitions {
784784
* When true, everyone can see the product when requested. When false it will
785785
* not be visible to anyone except those on the provider team.
786786
*/
787-
readonly public?: boolean;
787+
readonly public: boolean;
788788
/**
789789
* When true, the product will be displayed in product listings alongside
790790
* other products. When false the product will be excluded from listings,
791791
* but can still be provisioned directly if it's label is known.
792792
* Any pages that display information about the product when not listed,
793793
* should indicate to webcrawlers that the content should not be indexed.
794794
*/
795-
readonly listed?: boolean;
795+
readonly listed: boolean;
796796
/**
797797
* Object to hold various flags for marketing purposes only. These are values
798798
* that need to be stored, but should not affect decision making in code. If
799799
* we find ourselves in a position where we think they should, we should
800800
* consider refactoring our listing definition.
801801
*/
802-
readonly marketing?: {
802+
readonly marketing: {
803803
/**
804804
* Indicates whether or not the product is in `Beta` and should be
805805
* advertised as such. This does not have any impact on who can access the
806806
* product, it is just used to inform consumers through our clients.
807807
*/
808-
readonly beta?: boolean;
808+
readonly beta: boolean;
809809
/**
810810
* Indicates whether or not the product is in `New` and should be
811811
* advertised as such. This does not have any impact on who can access the
812812
* product, it is just used to inform consumers through our clients.
813813
*/
814-
readonly new?: boolean;
814+
readonly new: boolean;
815815
/**
816816
* Indicates whether or not the product is in `New` and should be
817817
* advertised as such. This does not have any impact on who can access the
818818
* product, it is just used to inform consumers through our clients.
819819
*/
820-
readonly featured?: boolean;
820+
readonly featured: boolean;
821821
};
822822
};
823823
/**
@@ -858,7 +858,7 @@ export interface definitions {
858858
* * `multiple`: Multiple credentials are supported at the same time.
859859
* * `unknown`: The credential type is unknown.
860860
*/
861-
readonly credential?: "none" | "single" | "multiple" | "unknown";
861+
readonly credential: "none" | "single" | "multiple" | "unknown";
862862
};
863863
readonly ProductBody: {
864864
readonly provider_id: definitions["ID"];

0 commit comments

Comments
 (0)