From 39162aa44c6316229d79c3956184c6073e00dc4c Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Wed, 10 Feb 2021 15:30:43 +0200 Subject: [PATCH 01/20] WIP on generating TS type declarations for operations --- src/generated-client.d.ts | 469 ++++++++++++++++++++++++++++ templates/generated-client.d.ts.hbs | 22 ++ templates/helpers/operation.js | 60 +++- templates/index.js | 5 + 4 files changed, 553 insertions(+), 3 deletions(-) create mode 100644 src/generated-client.d.ts create mode 100644 templates/generated-client.d.ts.hbs diff --git a/src/generated-client.d.ts b/src/generated-client.d.ts new file mode 100644 index 000000000..c4022bb03 --- /dev/null +++ b/src/generated-client.d.ts @@ -0,0 +1,469 @@ +/*! + * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. + * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and limitations under the License. + */ + + +export = GeneratedApiClient; + +declare class GeneratedApiClient { + listApplications(queryParameters: { + q: string, + after: string, + limit: string, + filter: string, + expand: string, + includeNonDeleted: string, + }): Promise; + createApplication(application: Application, queryParameters: { + activate: string, + }): Promise; + deleteApplication(appId: string): undefined; + getApplication(appId: string, queryParameters: { + expand: string, + }): Promise; + updateApplication(appId: string, application: Application): Promise; + listCsrsForApplication(appId: string): Promise; + generateCsrForApplication(appId: string, csrMetadata: CsrMetadata): Promise; + revokeCsrFromApplication(appId: string, csrId: string): undefined; + getCsrForApplication(appId: string, csrId: string): Promise; + publishCerCert(appId: string, csrId: string, certificate: string): Promise; + publishBinaryCerCert(appId: string, csrId: string, certificate: string): Promise; + publishDerCert(appId: string, csrId: string, certificate: string): Promise; + publishBinaryDerCert(appId: string, csrId: string, certificate: string): Promise; + publishBinaryPemCert(appId: string, csrId: string, certificate: string): Promise; + listApplicationKeys(appId: string): Promise; + generateApplicationKey(appId: string, queryParameters: { + validityYears: string, + }): Promise; + getApplicationKey(appId: string, keyId: string): Promise; + cloneApplicationKey(appId: string, keyId: string, queryParameters: { + targetAid: string, + }): Promise; + listScopeConsentGrants(appId: string, queryParameters: { + expand: string, + }): Promise; + grantConsentToScope(appId: string, oAuth2ScopeConsentGrant: OAuth2ScopeConsentGrant): Promise; + revokeScopeConsentGrant(appId: string, grantId: string): undefined; + getScopeConsentGrant(appId: string, grantId: string, queryParameters: { + expand: string, + }): Promise; + listApplicationGroupAssignments(appId: string, queryParameters: { + q: string, + after: string, + limit: string, + expand: string, + }): Promise; + deleteApplicationGroupAssignment(appId: string, groupId: string): undefined; + getApplicationGroupAssignment(appId: string, groupId: string, queryParameters: { + expand: string, + }): Promise; + createApplicationGroupAssignment(appId: string, groupId: string, applicationGroupAssignment: ApplicationGroupAssignment): Promise; + activateApplication(appId: string): undefined; + deactivateApplication(appId: string): undefined; + revokeOAuth2TokensForApplication(appId: string): undefined; + listOAuth2TokensForApplication(appId: string, queryParameters: { + expand: string, + after: string, + limit: string, + }): Promise; + revokeOAuth2TokenForApplication(appId: string, tokenId: string): undefined; + getOAuth2TokenForApplication(appId: string, tokenId: string, queryParameters: { + expand: string, + }): Promise; + listApplicationUsers(appId: string, queryParameters: { + q: string, + query_scope: string, + after: string, + limit: string, + filter: string, + expand: string, + }): Promise; + assignUserToApplication(appId: string, appUser: AppUser): Promise; + deleteApplicationUser(appId: string, userId: string, queryParameters: { + sendEmail: string, + }): undefined; + getApplicationUser(appId: string, userId: string, queryParameters: { + expand: string, + }): Promise; + updateApplicationUser(appId: string, userId: string, appUser: AppUser): Promise; + listAuthorizationServers(queryParameters: { + q: string, + limit: string, + after: string, + }): Promise; + createAuthorizationServer(authorizationServer: AuthorizationServer): Promise; + deleteAuthorizationServer(authServerId: string): undefined; + getAuthorizationServer(authServerId: string): Promise; + updateAuthorizationServer(authServerId: string, authorizationServer: AuthorizationServer): Promise; + listOAuth2Claims(authServerId: string): Promise; + createOAuth2Claim(authServerId: string, oAuth2Claim: OAuth2Claim): Promise; + deleteOAuth2Claim(authServerId: string, claimId: string): undefined; + getOAuth2Claim(authServerId: string, claimId: string): Promise; + updateOAuth2Claim(authServerId: string, claimId: string, oAuth2Claim: OAuth2Claim): Promise; + listOAuth2ClientsForAuthorizationServer(authServerId: string): Promise; + revokeRefreshTokensForAuthorizationServerAndClient(authServerId: string, clientId: string): undefined; + listRefreshTokensForAuthorizationServerAndClient(authServerId: string, clientId: string, queryParameters: { + expand: string, + after: string, + limit: string, + }): Promise; + revokeRefreshTokenForAuthorizationServerAndClient(authServerId: string, clientId: string, tokenId: string): undefined; + getRefreshTokenForAuthorizationServerAndClient(authServerId: string, clientId: string, tokenId: string, queryParameters: { + expand: string, + }): Promise; + listAuthorizationServerKeys(authServerId: string): Promise; + rotateAuthorizationServerKeys(authServerId: string): Promise; + activateAuthorizationServer(authServerId: string): undefined; + deactivateAuthorizationServer(authServerId: string): undefined; + listAuthorizationServerPolicies(authServerId: string): Promise; + createAuthorizationServerPolicy(authServerId: string, authorizationServerPolicy: AuthorizationServerPolicy): Promise; + deleteAuthorizationServerPolicy(authServerId: string, policyId: string): undefined; + getAuthorizationServerPolicy(authServerId: string, policyId: string): Promise; + updateAuthorizationServerPolicy(authServerId: string, policyId: string, authorizationServerPolicy: AuthorizationServerPolicy): Promise; + listAuthorizationServerPolicyRules(policyId: string, authServerId: string): Promise; + createAuthorizationServerPolicyRule(policyId: string, authServerId: string, authorizationServerPolicyRule: AuthorizationServerPolicyRule): Promise; + deleteAuthorizationServerPolicyRule(policyId: string, authServerId: string, ruleId: string): undefined; + getAuthorizationServerPolicyRule(policyId: string, authServerId: string, ruleId: string): Promise; + updateAuthorizationServerPolicyRule(policyId: string, authServerId: string, ruleId: string, authorizationServerPolicyRule: AuthorizationServerPolicyRule): Promise; + listOAuth2Scopes(authServerId: string, queryParameters: { + q: string, + filter: string, + cursor: string, + limit: string, + }): Promise; + createOAuth2Scope(authServerId: string, oAuth2Scope: OAuth2Scope): Promise; + deleteOAuth2Scope(authServerId: string, scopeId: string): undefined; + getOAuth2Scope(authServerId: string, scopeId: string): Promise; + updateOAuth2Scope(authServerId: string, scopeId: string, oAuth2Scope: OAuth2Scope): Promise; + listEventHooks(): Promise; + createEventHook(eventHook: EventHook): Promise; + deleteEventHook(eventHookId: string): undefined; + getEventHook(eventHookId: string): Promise; + updateEventHook(eventHookId: string, eventHook: EventHook): Promise; + activateEventHook(eventHookId: string): Promise; + deactivateEventHook(eventHookId: string): Promise; + verifyEventHook(eventHookId: string): Promise; + listFeatures(): Promise; + getFeature(featureId: string): Promise; + listFeatureDependencies(featureId: string): Promise; + listFeatureDependents(featureId: string): Promise; + updateFeatureLifecycle(featureId: string, lifecycle: string, queryParameters: { + mode: string, + }): Promise; + listGroups(queryParameters: { + q: string, + filter: string, + after: string, + limit: string, + expand: string, + }): Promise; + createGroup(group: Group): Promise; + listGroupRules(queryParameters: { + limit: string, + after: string, + search: string, + expand: string, + }): Promise; + createGroupRule(groupRule: GroupRule): Promise; + deleteGroupRule(ruleId: string): undefined; + getGroupRule(ruleId: string, queryParameters: { + expand: string, + }): Promise; + updateGroupRule(ruleId: string, groupRule: GroupRule): Promise; + activateGroupRule(ruleId: string): undefined; + deactivateGroupRule(ruleId: string): undefined; + deleteGroup(groupId: string): undefined; + getGroup(groupId: string): Promise; + updateGroup(groupId: string, group: Group): Promise; + listAssignedApplicationsForGroup(groupId: string, queryParameters: { + after: string, + limit: string, + }): Promise; + listGroupAssignedRoles(groupId: string, queryParameters: { + expand: string, + }): Promise; + assignRoleToGroup(groupId: string, assignRoleRequest: AssignRoleRequest, queryParameters: { + disableNotifications: string, + }): Promise; + removeRoleFromGroup(groupId: string, roleId: string): undefined; + getRole(groupId: string, roleId: string): Promise; + listApplicationTargetsForApplicationAdministratorRoleForGroup(groupId: string, roleId: string, queryParameters: { + after: string, + limit: string, + }): Promise; + removeApplicationTargetFromApplicationAdministratorRoleGivenToGroup(groupId: string, roleId: string, appName: string): undefined; + addApplicationTargetToAdminRoleGivenToGroup(groupId: string, roleId: string, appName: string): undefined; + removeApplicationTargetFromAdministratorRoleGivenToGroup(groupId: string, roleId: string, appName: string, applicationId: string): undefined; + addApplicationInstanceTargetToAppAdminRoleGivenToGroup(groupId: string, roleId: string, appName: string, applicationId: string): undefined; + listGroupTargetsForGroupRole(groupId: string, roleId: string, queryParameters: { + after: string, + limit: string, + }): Promise; + removeGroupTargetFromGroupAdministratorRoleGivenToGroup(groupId: string, roleId: string, targetGroupId: string): undefined; + addGroupTargetToGroupAdministratorRoleForGroup(groupId: string, roleId: string, targetGroupId: string): undefined; + listGroupUsers(groupId: string, queryParameters: { + after: string, + limit: string, + }): Promise; + removeUserFromGroup(groupId: string, userId: string): undefined; + addUserToGroup(groupId: string, userId: string): undefined; + listIdentityProviders(queryParameters: { + q: string, + after: string, + limit: string, + type: string, + }): Promise; + createIdentityProvider(identityProvider: IdentityProvider): Promise; + listIdentityProviderKeys(queryParameters: { + after: string, + limit: string, + }): Promise; + createIdentityProviderKey(jsonWebKey: JsonWebKey): Promise; + deleteIdentityProviderKey(keyId: string): undefined; + getIdentityProviderKey(keyId: string): Promise; + deleteIdentityProvider(idpId: string): undefined; + getIdentityProvider(idpId: string): Promise; + updateIdentityProvider(idpId: string, identityProvider: IdentityProvider): Promise; + listCsrsForIdentityProvider(idpId: string): Promise; + generateCsrForIdentityProvider(idpId: string, csrMetadata: CsrMetadata): Promise; + revokeCsrForIdentityProvider(idpId: string, csrId: string): undefined; + getCsrForIdentityProvider(idpId: string, csrId: string): Promise; + publishCerCertForIdentityProvider(idpId: string, csrId: string, certificate: string): Promise; + publishBinaryCerCertForIdentityProvider(idpId: string, csrId: string, certificate: string): Promise; + publishDerCertForIdentityProvider(idpId: string, csrId: string, certificate: string): Promise; + publishBinaryDerCertForIdentityProvider(idpId: string, csrId: string, certificate: string): Promise; + publishBinaryPemCertForIdentityProvider(idpId: string, csrId: string, certificate: string): Promise; + listIdentityProviderSigningKeys(idpId: string): Promise; + generateIdentityProviderSigningKey(idpId: string, queryParameters: { + validityYears: string, + }): Promise; + getIdentityProviderSigningKey(idpId: string, keyId: string): Promise; + cloneIdentityProviderKey(idpId: string, keyId: string, queryParameters: { + targetIdpId: string, + }): Promise; + activateIdentityProvider(idpId: string): Promise; + deactivateIdentityProvider(idpId: string): Promise; + listIdentityProviderApplicationUsers(idpId: string): Promise; + unlinkUserFromIdentityProvider(idpId: string, userId: string): undefined; + getIdentityProviderApplicationUser(idpId: string, userId: string): Promise; + linkUserToIdentityProvider(idpId: string, userId: string, userIdentityProviderLinkRequest: UserIdentityProviderLinkRequest): Promise; + listSocialAuthTokens(idpId: string, userId: string): Promise; + listInlineHooks(queryParameters: { + type: string, + }): Promise; + createInlineHook(inlineHook: InlineHook): Promise; + deleteInlineHook(inlineHookId: string): undefined; + getInlineHook(inlineHookId: string): Promise; + updateInlineHook(inlineHookId: string, inlineHook: InlineHook): Promise; + executeInlineHook(inlineHookId: string, inlineHookPayload: InlineHookPayload): Promise; + activateInlineHook(inlineHookId: string): Promise; + deactivateInlineHook(inlineHookId: string): Promise; + getLogs(queryParameters: { + since: string, + until: string, + filter: string, + q: string, + limit: string, + sortOrder: string, + after: string, + }): Promise; + listLinkedObjectDefinitions(): Promise; + addLinkedObjectDefinition(linkedObject: LinkedObject): Promise; + deleteLinkedObjectDefinition(linkedObjectName: string): undefined; + getLinkedObjectDefinition(linkedObjectName: string): Promise; + listUserTypes(): Promise; + createUserType(userType: UserType): Promise; + deleteUserType(typeId: string): undefined; + getUserType(typeId: string): Promise; + updateUserType(typeId: string, userType: UserType): Promise; + replaceUserType(typeId: string, userType: UserType): Promise; + listPolicies(queryParameters: { + type: string, + status: string, + expand: string, + }): Promise; + createPolicy(policy: Policy, queryParameters: { + activate: string, + }): Promise; + deletePolicy(policyId: string): undefined; + getPolicy(policyId: string, queryParameters: { + expand: string, + }): Promise; + updatePolicy(policyId: string, policy: Policy): Promise; + activatePolicy(policyId: string): undefined; + deactivatePolicy(policyId: string): undefined; + listPolicyRules(policyId: string): Promise; + createPolicyRule(policyId: string, policyRule: PolicyRule): Promise; + deletePolicyRule(policyId: string, ruleId: string): undefined; + getPolicyRule(policyId: string, ruleId: string): Promise; + updatePolicyRule(policyId: string, ruleId: string, policyRule: PolicyRule): Promise; + activatePolicyRule(policyId: string, ruleId: string): undefined; + deactivatePolicyRule(policyId: string, ruleId: string): undefined; + createSession(createSessionRequest: CreateSessionRequest): Promise; + endSession(sessionId: string): undefined; + getSession(sessionId: string): Promise; + refreshSession(sessionId: string): Promise; + listSmsTemplates(queryParameters: { + templateType: string, + }): Promise; + createSmsTemplate(smsTemplate: SmsTemplate): Promise; + deleteSmsTemplate(templateId: string): undefined; + getSmsTemplate(templateId: string): Promise; + partialUpdateSmsTemplate(templateId: string, smsTemplate: SmsTemplate): Promise; + updateSmsTemplate(templateId: string, smsTemplate: SmsTemplate): Promise; + listOrigins(queryParameters: { + q: string, + filter: string, + after: string, + limit: string, + }): Promise; + createOrigin(trustedOrigin: TrustedOrigin): Promise; + deleteOrigin(trustedOriginId: string): undefined; + getOrigin(trustedOriginId: string): Promise; + updateOrigin(trustedOriginId: string, trustedOrigin: TrustedOrigin): Promise; + activateOrigin(trustedOriginId: string): Promise; + deactivateOrigin(trustedOriginId: string): Promise; + listUsers(queryParameters: { + q: string, + after: string, + limit: string, + filter: string, + search: string, + sortBy: string, + sortOrder: string, + }): Promise; + createUser(createUserRequest: CreateUserRequest, queryParameters: { + activate: string, + provider: string, + nextLogin: string, + }): Promise; + setLinkedObjectForUser(associatedUserId: string, primaryRelationshipName: string, primaryUserId: string): undefined; + deactivateOrDeleteUser(userId: string, queryParameters: { + sendEmail: string, + }): undefined; + getUser(userId: string): Promise; + partialUpdateUser(userId: string, user: User, queryParameters: { + strict: string, + }): Promise; + updateUser(userId: string, user: User, queryParameters: { + strict: string, + }): Promise; + listAppLinks(userId: string): Promise; + listUserClients(userId: string): Promise; + revokeGrantsForUserAndClient(userId: string, clientId: string): undefined; + listGrantsForUserAndClient(userId: string, clientId: string, queryParameters: { + expand: string, + after: string, + limit: string, + }): Promise; + revokeTokensForUserAndClient(userId: string, clientId: string): undefined; + listRefreshTokensForUserAndClient(userId: string, clientId: string, queryParameters: { + expand: string, + after: string, + limit: string, + }): Promise; + revokeTokenForUserAndClient(userId: string, clientId: string, tokenId: string): undefined; + getRefreshTokenForUserAndClient(userId: string, clientId: string, tokenId: string, queryParameters: { + expand: string, + limit: string, + after: string, + }): Promise; + changePassword(userId: string, changePasswordRequest: ChangePasswordRequest, queryParameters: { + strict: string, + }): Promise; + changeRecoveryQuestion(userId: string, userCredentials: UserCredentials): Promise; + forgotPasswordGenerateOneTimeToken(userId: string, queryParameters: { + sendEmail: string, + }): Promise; + forgotPasswordSetNewPassword(userId: string, userCredentials: UserCredentials, queryParameters: { + sendEmail: string, + }): Promise; + listFactors(userId: string): Promise; + enrollFactor(userId: string, userFactor: UserFactor, queryParameters: { + updatePhone: string, + templateId: string, + tokenLifetimeSeconds: string, + activate: string, + }): Promise; + listSupportedFactors(userId: string): Promise; + listSupportedSecurityQuestions(userId: string): Promise; + deleteFactor(userId: string, factorId: string): undefined; + getFactor(userId: string, factorId: string): Promise; + activateFactor(userId: string, factorId: string, activateFactorRequest: ActivateFactorRequest): Promise; + getFactorTransactionStatus(userId: string, factorId: string, transactionId: string): Promise; + verifyFactor(userId: string, factorId: string, verifyFactorRequest: VerifyFactorRequest, queryParameters: { + templateId: string, + tokenLifetimeSeconds: string, + }): Promise; + revokeUserGrants(userId: string): undefined; + listUserGrants(userId: string, queryParameters: { + scopeId: string, + expand: string, + after: string, + limit: string, + }): Promise; + revokeUserGrant(userId: string, grantId: string): undefined; + getUserGrant(userId: string, grantId: string, queryParameters: { + expand: string, + }): Promise; + listUserGroups(userId: string): Promise; + listUserIdentityProviders(userId: string): Promise; + activateUser(userId: string, queryParameters: { + sendEmail: string, + }): Promise; + deactivateUser(userId: string, queryParameters: { + sendEmail: string, + }): undefined; + expirePassword(userId: string): Promise; + expirePasswordAndGetTemporaryPassword(userId: string): Promise; + reactivateUser(userId: string, queryParameters: { + sendEmail: string, + }): Promise; + resetFactors(userId: string): undefined; + resetPassword(userId: string, queryParameters: { + sendEmail: string, + }): Promise; + suspendUser(userId: string): undefined; + unlockUser(userId: string): undefined; + unsuspendUser(userId: string): undefined; + removeLinkedObjectForUser(userId: string, relationshipName: string): undefined; + getLinkedObjectsForUser(userId: string, relationshipName: string, queryParameters: { + after: string, + limit: string, + }): Promise; + listAssignedRolesForUser(userId: string, queryParameters: { + expand: string, + }): Promise; + assignRoleToUser(userId: string, assignRoleRequest: AssignRoleRequest, queryParameters: { + disableNotifications: string, + }): Promise; + removeRoleFromUser(userId: string, roleId: string): undefined; + listApplicationTargetsForApplicationAdministratorRoleForUser(userId: string, roleId: string, queryParameters: { + after: string, + limit: string, + }): Promise; + addAllAppsAsTargetToRole(userId: string, roleId: string): undefined; + removeApplicationTargetFromApplicationAdministratorRoleForUser(userId: string, roleId: string, appName: string): undefined; + addApplicationTargetToAdminRoleForUser(userId: string, roleId: string, appName: string): undefined; + removeApplicationTargetFromAdministratorRoleForUser(userId: string, roleId: string, appName: string, applicationId: string): undefined; + addApplicationTargetToAppAdminRoleForUser(userId: string, roleId: string, appName: string, applicationId: string): undefined; + listGroupTargetsForRole(userId: string, roleId: string, queryParameters: { + after: string, + limit: string, + }): Promise; + removeGroupTargetFromRole(userId: string, roleId: string, groupId: string): undefined; + addGroupTargetToRole(userId: string, roleId: string, groupId: string): undefined; + clearUserSessions(userId: string, queryParameters: { + oauthTokens: string, + }): undefined; +} + +module.exports = GeneratedApiClient; diff --git a/templates/generated-client.d.ts.hbs b/templates/generated-client.d.ts.hbs new file mode 100644 index 000000000..7008ad29f --- /dev/null +++ b/templates/generated-client.d.ts.hbs @@ -0,0 +1,22 @@ +/*! + * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. + * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and limitations under the License. + */ + + +export = GeneratedApiClient; + +declare class GeneratedApiClient { +{{#each operations}} + {{{typeScriptTypeDefinitionBuilder this}}} +{{/each}} +} + +module.exports = GeneratedApiClient; diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index 4c3a1c3a6..92fa4ef10 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -1,6 +1,6 @@ const _ = require('lodash'); -const getBodyModelNameInCamelCase = operation => { +const getBodyModelName = operation => { const { bodyModel, parameters } = operation; let bodyModelName = bodyModel; if (bodyModel === 'string') { @@ -9,9 +9,11 @@ const getBodyModelNameInCamelCase = operation => { bodyModelName = bodyParam.name; } } - return _.camelCase(bodyModelName); + return bodyModelName; }; +const getBodyModelNameInCamelCase = operation => _.camelCase(getBodyModelName(operation)); + const getOperationArgument = operation => { const { bodyModel, method, pathParams, queryParams } = operation; @@ -128,6 +130,57 @@ const jsdocBuilder = (operation) => { return lines.join('\n'); }; +const typeScriptTypeDefinitionBuilder = (operation) => { + let parameters = {}; + if (operation.pathParams.length) { + parameters = operation.pathParams.reduce((memo, parameter) => { + return { + ...memo, + [parameter.name]: 'string' + } + }, {}) + } + + if (!operation.isArray && operation.bodyModel) { + const bodyModelName = getBodyModelName(operation); + if (bodyModelName) { + parameters = { + ...parameters, + [_.camelCase(bodyModelName)]: operation.bodyModel + }; + } + } + + let queryParameters; + if (operation.queryParams.length) { + queryParameters = operation.queryParams.map(({name}) => name) + + let queryParametersString = '{ \n'; + queryParameters.forEach(parameter => { + queryParametersString += ` ${parameter}: string,\n` + }) + queryParametersString += ' }' + + parameters = { + ...parameters, + queryParameters: queryParametersString + } + } + let returnType; + if (operation.responseModel) { + if (operation.isArray) { + returnType = 'Promise'; + } else { + returnType = `Promise<${operation.responseModel}>`; + } + } + const parametersString = Object.keys(parameters).reduce((memo, key, index, collection) => { + let isFinalParameter = index === collection.length - 1; + return memo + `${key}: ${parameters[key]}${isFinalParameter ? '' : ', '}` + }, ''); + return `${operation.operationId}(${parametersString}): ${returnType};`; +}; + module.exports = { getBodyModelNameInCamelCase, operationArgumentBuilder, @@ -136,5 +189,6 @@ module.exports = { hasHeaders, getHttpMethod, shouldResolveJson, - jsdocBuilder + jsdocBuilder, + typeScriptTypeDefinitionBuilder, } diff --git a/templates/index.js b/templates/index.js index dc177ace8..d3a762cdb 100644 --- a/templates/index.js +++ b/templates/index.js @@ -50,6 +50,11 @@ js.process = ({spec, operations, models, handlebars}) => { } } }); + templates.push({ + src: 'generated-client.d.ts.hbs', + dest: 'src/generated-client.d.ts', + context: {operations, spec} + }); templates.push({ src: 'generated-client.js.hbs', From 863684d2936dd184dc739836cc51c64ed24b7e85 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Sun, 14 Feb 2021 22:39:32 +0200 Subject: [PATCH 02/20] add templates for type files and add template helper functions --- templates/factories.index.d.ts.hbs | 19 ++++ templates/factory.d.ts.hbs | 21 ++++ templates/generated-client.d.ts.hbs | 5 +- templates/index.js | 144 ++++++++++++++++++++++++++-- templates/model.d.ts.hbs | 53 ++++++++++ templates/model.index.d.ts.hbs | 19 ++++ 6 files changed, 251 insertions(+), 10 deletions(-) create mode 100644 templates/factories.index.d.ts.hbs create mode 100644 templates/factory.d.ts.hbs create mode 100644 templates/model.d.ts.hbs create mode 100644 templates/model.index.d.ts.hbs diff --git a/templates/factories.index.d.ts.hbs b/templates/factories.index.d.ts.hbs new file mode 100644 index 000000000..349ef4087 --- /dev/null +++ b/templates/factories.index.d.ts.hbs @@ -0,0 +1,19 @@ +/*! + * Copyright (c) 2017-2021, Okta, Inc. and/or its affiliates. All rights reserved. + * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and limitations under the License. + */ + +const factories = { +{{#each models}} + {{modelName}}: import('./{{modelName}}Factory'), +{{/each}} +} + +export = factories; diff --git a/templates/factory.d.ts.hbs b/templates/factory.d.ts.hbs new file mode 100644 index 000000000..5699b7c88 --- /dev/null +++ b/templates/factory.d.ts.hbs @@ -0,0 +1,21 @@ +/*! + * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. + * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and limitations under the License. + */ + +import ModelResolutionFactory from '../resolution-factory'; + +declare class {{parentModelName}}Factory extends ModelResolutionFactory { + getMapping(): object; + + getResolutionProperty(): string; +} + +export = {{parentModelName}}Factory; diff --git a/templates/generated-client.d.ts.hbs b/templates/generated-client.d.ts.hbs index 7008ad29f..a2fa6be7b 100644 --- a/templates/generated-client.d.ts.hbs +++ b/templates/generated-client.d.ts.hbs @@ -10,8 +10,7 @@ * See the License for the specific language governing permissions and limitations under the License. */ - -export = GeneratedApiClient; +{{{operationsArgumentsImportBuilder operations null}}} declare class GeneratedApiClient { {{#each operations}} @@ -19,4 +18,4 @@ declare class GeneratedApiClient { {{/each}} } -module.exports = GeneratedApiClient; +export = GeneratedApiClient; diff --git a/templates/index.js b/templates/index.js index d3a762cdb..ff777e74f 100644 --- a/templates/index.js +++ b/templates/index.js @@ -52,7 +52,7 @@ js.process = ({spec, operations, models, handlebars}) => { }); templates.push({ src: 'generated-client.d.ts.hbs', - dest: 'src/generated-client.d.ts', + dest: 'src/types/generated-client.d.ts', context: {operations, spec} }); @@ -70,6 +70,12 @@ js.process = ({spec, operations, models, handlebars}) => { context: model }); + templates.push({ + src: 'model.d.ts.hbs', + dest: `src/types/models/${model.modelName}.d.ts`, + context: model + }); + if (model.resolutionStrategy) { const mapping = Object.entries(model.resolutionStrategy.valueToModelMapping).map(([propertyValue, className]) => { const classModel = models.filter(model => model.modelName === className)[0]; @@ -84,6 +90,15 @@ js.process = ({spec, operations, models, handlebars}) => { propertyName: model.resolutionStrategy.propertyName } }); + templates.push({ + src: 'factory.d.ts.hbs', + dest: `src/types/factories/${model.modelName}Factory.d.ts`, + context: { + parentModelName: model.modelName, + mapping, + propertyName: model.resolutionStrategy.propertyName + } + }); } } @@ -93,17 +108,47 @@ js.process = ({spec, operations, models, handlebars}) => { context: { models } }); + templates.push({ + src: 'model.index.d.ts.hbs', + dest: 'src/types/models/index.d.ts', + context: { models } + }); + templates.push({ src: 'factories.index.js.hbs', dest: 'src/factories/index.js', context: { models: models.filter(model => model.requiresResolution) } }); + templates.push({ + src: 'factories.index.d.ts.hbs', + dest: 'src/types/factories/index.d.ts', + context: { models: models.filter(model => model.requiresResolution) } + }); + // Add helpers // Register Operation helpers Object.keys(operationUtils).forEach(key => handlebars.registerHelper(key, operationUtils[key])); + handlebars.registerHelper('sanitizeModelPropertyName', propertyName => { + const restrictedChars = ['#']; + const knownConflictingPropertyNames = ['verify']; + let sanitizedPropertyName = propertyName; + + const containsRestrictedChars = restrictedChars.find(char => propertyName.includes(char)); + + if (knownConflictingPropertyNames.includes(propertyName)) { + sanitizedPropertyName = `_${propertyName}`; + } + + if (containsRestrictedChars) { + sanitizedPropertyName = `'${propertyName}'`; + } + + return sanitizedPropertyName; + }); + // TODO: move helpers to modules const paramMatcher = /{(.*?)}/g; handlebars.registerHelper('replacePathParams', (path) => { @@ -118,15 +163,59 @@ js.process = ({spec, operations, models, handlebars}) => { return new handlebars.SafeString(path); }); - handlebars.registerHelper('modelImportBuilder', (model) => { + const operationsArgumentsImportBuilder = (operations, model) => { + const importStatements = new Set(); + const pathPrefix = model ? '' : '/models' + operations.forEach(operation => { + if (!MODELS_SHOULD_NOT_PROCESS.includes(operation.bodyModel) && + (operation.method === 'post' || operation.method === 'put') && + operation.bodyModel ) { + if (model && operation.bodyModel !== model.modelName) { + importStatements.add(`import ${operation.bodyModel} from'.${pathPrefix}/${operation.bodyModel}';`); + } else if (!model) { + importStatements.add(`import ${operation.bodyModel} from'.${pathPrefix}/${operation.bodyModel}';`); + } + } + + if (operation.responseModel) { + if (operation.isArray) { + importStatements.add(`import Collection from '../collection';`); + } else if(model && operation.responseModel !== model.modelName) { + importStatements.add(`import ${operation.responseModel} from'.${pathPrefix}/${operation.responseModel}';`); + } else if (!model){ + importStatements.add(`import ${operation.responseModel} from'.${pathPrefix}/${operation.responseModel}';`); + } + } + }); + + return model ? importStatements : Array.from(importStatements).join('\n'); + }; + + handlebars.registerHelper('operationsArgumentsImportBuilder', operationsArgumentsImportBuilder); + + handlebars.registerHelper('modelImportBuilder', (model, importSyntax='cjs') => { if (!model.properties) { return; } - const importStatements = new Set(); + let importStatements = new Set(); + + if (importSyntax === 'ts') { + const operations = model.methods.map(method => method.operation); + importStatements = new Set([...operationsArgumentsImportBuilder(operations, model)]); + } + model.properties.forEach(property => { const shouldProcess = !MODELS_SHOULD_NOT_PROCESS.includes(property.model); - if (property.$ref && shouldProcess && !property.isEnum) { - importStatements.add(`const ${property.model} = require('./${property.model}');`); + if (property.$ref && shouldProcess) { + if (importSyntax === 'ts') { + if (model.methods.length > 1) { + } + importStatements.add(`import ${property.model} from'./${property.model}';`); + } else { + if (!property.isEnum) { + importStatements.add(`const ${property.model} = require('./${property.model}');`); + } + } } }); return Array.from(importStatements).join('\n'); @@ -149,9 +238,7 @@ js.process = ({spec, operations, models, handlebars}) => { }); handlebars.registerHelper('modelMethodPublicArgumentBuilder', (method, modelName) => { - const args = []; - const operation = method.operation; operation.pathParams.forEach(param => { @@ -235,6 +322,49 @@ js.process = ({spec, operations, models, handlebars}) => { return output; }); + handlebars.registerHelper('convertSwaggerToTSType', (swaggerType) => { + return { + array: '[]', + integer: 'number', + hash: '{\n\ [name: string]: unknown;\n\ }', + dateTime: 'string', + password: 'string', + }[swaggerType] || swaggerType; + }); + + handlebars.registerHelper('modelMethodPublicArgumentTypeScriptTypingBuilder', (method, modelName) => { + const args = []; + + const operation = method.operation; + + operation.pathParams.forEach(param => { + const matchingArgument = method.arguments.filter(argument => argument.dest === param.name)[0]; + if (!matchingArgument || !matchingArgument.src){ + args.push(`${param.name}: ${param.type}`); + } + }); + + if ((operation.method === 'post' || operation.method === 'put') && operation.bodyModel && (operation.bodyModel !== modelName)) { + args.push(`${_.camelCase(operation.bodyModel)}: ${operation.bodyModel}`); + } + + if (operation.queryParams.length) { + args.push('queryParameters: object'); + } + + let returnType = 'undefined'; + if (operation.responseModel) { + if (operation.isArray) { + returnType = 'Promise'; + } else { + returnType = `Promise<${operation.responseModel}>`; + } + } + + const output = `(${args.join(', ')}): ${returnType};`; + return output; + }); + handlebars.registerHelper('getAffectedResources', (path) => { const resources = []; let pl = path.length; diff --git a/templates/model.d.ts.hbs b/templates/model.d.ts.hbs new file mode 100644 index 000000000..f3b5d253a --- /dev/null +++ b/templates/model.d.ts.hbs @@ -0,0 +1,53 @@ +/*! + * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. + * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and limitations under the License. + */ + + +/* THIS FILE IS AUTO-GENERATED - SEE CONTRIBUTOR DOCUMENTATION */ + +{{#if extends~}} +import {{extends}} from './{{extends}}'; +{{else}} +import Resource from '../../resource'; +{{/if}} +{{{modelImportBuilder this 'ts'}}} + +{{#if extends}} +declare class {{modelName}} extends {{extends}} { +{{else}} +declare class {{modelName}} extends Resource { +{{/if}} + constructor(resourceJson: string, client: any); + +{{#each properties}} +{{#if this.$ref}} +{{#if (ne this.model "object") }} + {{{sanitizeModelPropertyName this.propertyName}}}: {{model}}; +{{/if}} +{{else}} + {{{sanitizeModelPropertyName this.propertyName}}}: {{convertSwaggerToTSType this.commonType}}; +{{/if}} +{{/each}} + + {{#each crud}} + {{#if (eq alias 'update')}} + {{alias}}{{{modelMethodPublicArgumentTypeScriptTypingBuilder this ../modelName}}} + {{/if}} + {{#if (eq alias 'delete')}} + {{alias}}{{{modelMethodPublicArgumentTypeScriptTypingBuilder this ../modelName}}} + {{/if}} + {{/each}} + {{#each methods}} + {{alias}}{{{modelMethodPublicArgumentTypeScriptTypingBuilder this ../modelName}}} + {{/each}} +} + +export default {{modelName}}; diff --git a/templates/model.index.d.ts.hbs b/templates/model.index.d.ts.hbs new file mode 100644 index 000000000..0455bf2cb --- /dev/null +++ b/templates/model.index.d.ts.hbs @@ -0,0 +1,19 @@ +/*! + * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. + * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and limitations under the License. + */ + + +/** + * THIS FILE IS AUTO-GENERATED - SEE CONTRIBUTOR DOCUMENTATION + */ +{{#each models}} +export * from './{{modelName}}'; +{{/each}} From e9eaddd5877676ebc95b116a1346519587704de8 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Sun, 14 Feb 2021 22:48:05 +0200 Subject: [PATCH 03/20] move typings to src/types --- package.json | 1 + src/{ => types}/generated-client.d.ts | 52 +++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) rename src/{ => types}/generated-client.d.ts (90%) diff --git a/package.json b/package.json index 44ead43e4..f52a9f9bf 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "README.md" ], "main": "src/index.js", + "types": "src/types/index.d.ts", "scripts": { "banners": "./utils/maintain-banners.js", "prebuild": "rimraf ./src/models ./src/factories ./src/generated-client.js", diff --git a/src/generated-client.d.ts b/src/types/generated-client.d.ts similarity index 90% rename from src/generated-client.d.ts rename to src/types/generated-client.d.ts index c4022bb03..40022218b 100644 --- a/src/generated-client.d.ts +++ b/src/types/generated-client.d.ts @@ -10,8 +10,54 @@ * See the License for the specific language governing permissions and limitations under the License. */ - -export = GeneratedApiClient; +import Collection from '../collection'; +import Application from'./models/Application'; +import CsrMetadata from'./models/CsrMetadata'; +import Csr from'./models/Csr'; +import JsonWebKey from'./models/JsonWebKey'; +import OAuth2ScopeConsentGrant from'./models/OAuth2ScopeConsentGrant'; +import ApplicationGroupAssignment from'./models/ApplicationGroupAssignment'; +import OAuth2Token from'./models/OAuth2Token'; +import AppUser from'./models/AppUser'; +import AuthorizationServer from'./models/AuthorizationServer'; +import OAuth2Claim from'./models/OAuth2Claim'; +import OAuth2RefreshToken from'./models/OAuth2RefreshToken'; +import JwkUse from'./models/JwkUse'; +import AuthorizationServerPolicy from'./models/AuthorizationServerPolicy'; +import AuthorizationServerPolicyRule from'./models/AuthorizationServerPolicyRule'; +import OAuth2Scope from'./models/OAuth2Scope'; +import EventHook from'./models/EventHook'; +import Feature from'./models/Feature'; +import Group from'./models/Group'; +import GroupRule from'./models/GroupRule'; +import AssignRoleRequest from'./models/AssignRoleRequest'; +import Role from'./models/Role'; +import IdentityProvider from'./models/IdentityProvider'; +import IdentityProviderApplicationUser from'./models/IdentityProviderApplicationUser'; +import UserIdentityProviderLinkRequest from'./models/UserIdentityProviderLinkRequest'; +import InlineHook from'./models/InlineHook'; +import InlineHookPayload from'./models/InlineHookPayload'; +import InlineHookResponse from'./models/InlineHookResponse'; +import LinkedObject from'./models/LinkedObject'; +import UserType from'./models/UserType'; +import Policy from'./models/Policy'; +import PolicyRule from'./models/PolicyRule'; +import CreateSessionRequest from'./models/CreateSessionRequest'; +import Session from'./models/Session'; +import SmsTemplate from'./models/SmsTemplate'; +import TrustedOrigin from'./models/TrustedOrigin'; +import CreateUserRequest from'./models/CreateUserRequest'; +import User from'./models/User'; +import ChangePasswordRequest from'./models/ChangePasswordRequest'; +import UserCredentials from'./models/UserCredentials'; +import ForgotPasswordResponse from'./models/ForgotPasswordResponse'; +import UserFactor from'./models/UserFactor'; +import ActivateFactorRequest from'./models/ActivateFactorRequest'; +import VerifyUserFactorResponse from'./models/VerifyUserFactorResponse'; +import VerifyFactorRequest from'./models/VerifyFactorRequest'; +import UserActivationToken from'./models/UserActivationToken'; +import TempPassword from'./models/TempPassword'; +import ResetPasswordToken from'./models/ResetPasswordToken'; declare class GeneratedApiClient { listApplications(queryParameters: { @@ -466,4 +512,4 @@ declare class GeneratedApiClient { }): undefined; } -module.exports = GeneratedApiClient; +export = GeneratedApiClient; From a119473c62b5f4f0cef06f153d728f1515311da2 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Mon, 15 Feb 2021 15:00:39 +0200 Subject: [PATCH 04/20] break down TypeScript signature formatter for genrated client --- templates/generated-client.d.ts.hbs | 2 +- templates/helpers/operation.js | 81 ++++++++++++++++------------- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/templates/generated-client.d.ts.hbs b/templates/generated-client.d.ts.hbs index a2fa6be7b..579875031 100644 --- a/templates/generated-client.d.ts.hbs +++ b/templates/generated-client.d.ts.hbs @@ -14,7 +14,7 @@ declare class GeneratedApiClient { {{#each operations}} - {{{typeScriptTypeDefinitionBuilder this}}} + {{{typeScriptSignatureBuilder this}}} {{/each}} } diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index 92fa4ef10..c8726301f 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -130,43 +130,29 @@ const jsdocBuilder = (operation) => { return lines.join('\n'); }; -const typeScriptTypeDefinitionBuilder = (operation) => { - let parameters = {}; - if (operation.pathParams.length) { - parameters = operation.pathParams.reduce((memo, parameter) => { - return { - ...memo, - [parameter.name]: 'string' - } - }, {}) - } +const getOperationTypeScriptSignature = operation => { + const { bodyModel, method, pathParams, queryParams } = operation; + const args = new Map(); - if (!operation.isArray && operation.bodyModel) { + pathParams.forEach(pathParam => { + args.set(pathParam.name, 'string') + }); + + if ((method === 'post' || method === 'put') && bodyModel) { const bodyModelName = getBodyModelName(operation); if (bodyModelName) { - parameters = { - ...parameters, - [_.camelCase(bodyModelName)]: operation.bodyModel - }; + args.set(_.camelCase(bodyModelName), operation.bodyModel); } } - let queryParameters; - if (operation.queryParams.length) { - queryParameters = operation.queryParams.map(({name}) => name) - - let queryParametersString = '{ \n'; - queryParameters.forEach(parameter => { - queryParametersString += ` ${parameter}: string,\n` - }) - queryParametersString += ' }' - - parameters = { - ...parameters, - queryParameters: queryParametersString - } + if (queryParams.length) { + args.set('queryParameters', queryParams.reduce((acc, param) => { + acc.push(param.name); + return acc; + }, [])); } - let returnType; + + let returnType = 'undefined'; if (operation.responseModel) { if (operation.isArray) { returnType = 'Promise'; @@ -174,11 +160,34 @@ const typeScriptTypeDefinitionBuilder = (operation) => { returnType = `Promise<${operation.responseModel}>`; } } - const parametersString = Object.keys(parameters).reduce((memo, key, index, collection) => { - let isFinalParameter = index === collection.length - 1; - return memo + `${key}: ${parameters[key]}${isFinalParameter ? '' : ', '}` - }, ''); - return `${operation.operationId}(${parametersString}): ${returnType};`; + + return [args, returnType]; +}; + + +const formatObjectLiteralType = typeProps => { + let objectLiteralType = '{ \n'; + typeProps.forEach(prop => { + objectLiteralType += ` ${prop}: string,\n` + }) + objectLiteralType += ' }'; + console.log(objectLiteralType) + return objectLiteralType; +} + +const typeScriptSignatureBuilder = (operation) => { + const [args, returnType] = getOperationTypeScriptSignature(operation); + + const typedArgs = []; + for (let [arg, argType] of args) { + if (Array.isArray(argType)) { + typedArgs.push(`${arg}: ${formatObjectLiteralType(argType)}`) + } else { + typedArgs.push(`${arg}: ${argType}`) + } + } + + return `${operation.operationId}(${typedArgs.join(', ')}): ${returnType};`; }; module.exports = { @@ -190,5 +199,5 @@ module.exports = { getHttpMethod, shouldResolveJson, jsdocBuilder, - typeScriptTypeDefinitionBuilder, + typeScriptSignatureBuilder, } From e19e7a77d991df382d338f6af490c55c130384b4 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Mon, 15 Feb 2021 18:56:35 +0200 Subject: [PATCH 05/20] re-use signature getter for models and generated client --- templates/generated-client.d.ts.hbs | 4 +- templates/helpers/model.js | 0 templates/helpers/operation.js | 110 ++++++++++++++++++++++++++-- templates/index.js | 85 +-------------------- templates/model.d.ts.hbs | 8 +- 5 files changed, 112 insertions(+), 95 deletions(-) create mode 100644 templates/helpers/model.js diff --git a/templates/generated-client.d.ts.hbs b/templates/generated-client.d.ts.hbs index 579875031..27a6c8dc2 100644 --- a/templates/generated-client.d.ts.hbs +++ b/templates/generated-client.d.ts.hbs @@ -10,11 +10,11 @@ * See the License for the specific language governing permissions and limitations under the License. */ -{{{operationsArgumentsImportBuilder operations null}}} +{{{typeScriptClientImportBuilder operations}}} declare class GeneratedApiClient { {{#each operations}} - {{{typeScriptSignatureBuilder this}}} + {{{typeScriptOperationSignatureBuilder this}}} {{/each}} } diff --git a/templates/helpers/model.js b/templates/helpers/model.js new file mode 100644 index 000000000..e69de29bb diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index c8726301f..04d974342 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -1,5 +1,7 @@ const _ = require('lodash'); +const MODELS_SHOULD_NOT_PROCESS = ['object', 'string', 'undefined']; + const getBodyModelName = operation => { const { bodyModel, parameters } = operation; let bodyModelName = bodyModel; @@ -130,7 +132,7 @@ const jsdocBuilder = (operation) => { return lines.join('\n'); }; -const getOperationTypeScriptSignature = operation => { +const getOperationArgumentsAndReturnType = operation => { const { bodyModel, method, pathParams, queryParams } = operation; const args = new Map(); @@ -155,15 +157,32 @@ const getOperationTypeScriptSignature = operation => { let returnType = 'undefined'; if (operation.responseModel) { if (operation.isArray) { - returnType = 'Promise'; + returnType = 'Collection'; } else { - returnType = `Promise<${operation.responseModel}>`; + returnType = operation.responseModel; } } return [args, returnType]; }; +const getModelMethodArgumentsAndReturnType = (method, modelName) => { + const { operation, arguments } = method; + const [args, returnType] = getOperationArgumentsAndReturnType(operation); + + operation.pathParams.forEach(param => { + const matchingArgument = arguments.find(argument => argument.dest === param.name); + if (matchingArgument){ + args.delete(param.name); + } + }); + + const bodyModelName = getBodyModelName(operation); + if (bodyModelName && bodyModelName === modelName) { + args.delete(_.camelCase(operation.bodyModel)); + } + return [args, returnType]; +}; const formatObjectLiteralType = typeProps => { let objectLiteralType = '{ \n'; @@ -171,12 +190,83 @@ const formatObjectLiteralType = typeProps => { objectLiteralType += ` ${prop}: string,\n` }) objectLiteralType += ' }'; - console.log(objectLiteralType) return objectLiteralType; } -const typeScriptSignatureBuilder = (operation) => { - const [args, returnType] = getOperationTypeScriptSignature(operation); +const typeScriptOperationSignatureBuilder = (operation) => { + const [args, returnType] = getOperationArgumentsAndReturnType(operation); + + const typedArgs = []; + for (let [arg, argType] of args) { + if (Array.isArray(argType)) { + typedArgs.push(`${arg}: ${formatObjectLiteralType(argType)}`) + } else { + typedArgs.push(`${arg}: ${argType}`) + } + } + + return `${operation.operationId}(${typedArgs.join(', ')}): ${returnType === 'undefined' ? 'undefined' : `Promise<${returnType}>;`}`; +}; + +const typeScriptClientImportBuilder = (operations) => { + const operationsImportTypes = operations.reduce((acc, operation) => { + const [args, returnType] = getOperationArgumentsAndReturnType(operation); + return [ + ...acc, + ...args.values(), + returnType, + ] + }, []); + + const importStatements = []; + operationsImportTypes.forEach(type => { + if (!MODELS_SHOULD_NOT_PROCESS.includes(type) && !Array.isArray(type)) { + if (type === 'Collection') { + importStatements.push(`import Collection from '../collection';`); + } else { + importStatements.push(`import ${type} from './${type}';`); + } + } + }); + return importStatements.join('\n'); +}; + +const typeScriptModelImportBuilder = model => { + const {properties, methods} = model; + if (!properties && !methods) { + return; + } + + const methodsImportTypes = model.methods.reduce((acc, method) => { + const [args, returnType] = getModelMethodArgumentsAndReturnType(method, model.modelName); + return [ + ...acc, + ...args.values(), + returnType, + ] + }, []); + + const propertiesImportTypes = + properties.filter(property => property.$ref).map(property => property.model); + + const importTypes = new Set([...methodsImportTypes, ...propertiesImportTypes]); + const importStatements = []; + importTypes.forEach(type => { + if (!MODELS_SHOULD_NOT_PROCESS.includes(type) && !Array.isArray(type)) { + if (type === 'Collection') { + importStatements.push(`import Collection from '../collection';`); + } else { + importStatements.push(`import ${type} from './${type}';`); + } + } + }); + return importStatements.join('\n'); +}; + + +const typeScriptModelMethodSignatureBuilder = (method, modelName) => { + console.log(method.operation.operationId) + const [args, returnType] = getModelMethodArgumentsAndReturnType(method, modelName); const typedArgs = []; for (let [arg, argType] of args) { @@ -187,9 +277,10 @@ const typeScriptSignatureBuilder = (operation) => { } } - return `${operation.operationId}(${typedArgs.join(', ')}): ${returnType};`; + return `(${typedArgs.join(', ')}): ${returnType === 'undefined' ? 'undefined' : `Promise<${returnType}>;`}`; }; + module.exports = { getBodyModelNameInCamelCase, operationArgumentBuilder, @@ -199,5 +290,8 @@ module.exports = { getHttpMethod, shouldResolveJson, jsdocBuilder, - typeScriptSignatureBuilder, + typeScriptOperationSignatureBuilder, + typeScriptModelImportBuilder, + typeScriptModelMethodSignatureBuilder, + typeScriptClientImportBuilder, } diff --git a/templates/index.js b/templates/index.js index ff777e74f..3f3453174 100644 --- a/templates/index.js +++ b/templates/index.js @@ -163,59 +163,15 @@ js.process = ({spec, operations, models, handlebars}) => { return new handlebars.SafeString(path); }); - const operationsArgumentsImportBuilder = (operations, model) => { - const importStatements = new Set(); - const pathPrefix = model ? '' : '/models' - operations.forEach(operation => { - if (!MODELS_SHOULD_NOT_PROCESS.includes(operation.bodyModel) && - (operation.method === 'post' || operation.method === 'put') && - operation.bodyModel ) { - if (model && operation.bodyModel !== model.modelName) { - importStatements.add(`import ${operation.bodyModel} from'.${pathPrefix}/${operation.bodyModel}';`); - } else if (!model) { - importStatements.add(`import ${operation.bodyModel} from'.${pathPrefix}/${operation.bodyModel}';`); - } - } - - if (operation.responseModel) { - if (operation.isArray) { - importStatements.add(`import Collection from '../collection';`); - } else if(model && operation.responseModel !== model.modelName) { - importStatements.add(`import ${operation.responseModel} from'.${pathPrefix}/${operation.responseModel}';`); - } else if (!model){ - importStatements.add(`import ${operation.responseModel} from'.${pathPrefix}/${operation.responseModel}';`); - } - } - }); - - return model ? importStatements : Array.from(importStatements).join('\n'); - }; - - handlebars.registerHelper('operationsArgumentsImportBuilder', operationsArgumentsImportBuilder); - - handlebars.registerHelper('modelImportBuilder', (model, importSyntax='cjs') => { + handlebars.registerHelper('modelImportBuilder', model => { if (!model.properties) { return; } - let importStatements = new Set(); - - if (importSyntax === 'ts') { - const operations = model.methods.map(method => method.operation); - importStatements = new Set([...operationsArgumentsImportBuilder(operations, model)]); - } - + const importStatements = new Set(); model.properties.forEach(property => { const shouldProcess = !MODELS_SHOULD_NOT_PROCESS.includes(property.model); - if (property.$ref && shouldProcess) { - if (importSyntax === 'ts') { - if (model.methods.length > 1) { - } - importStatements.add(`import ${property.model} from'./${property.model}';`); - } else { - if (!property.isEnum) { - importStatements.add(`const ${property.model} = require('./${property.model}');`); - } - } + if (property.$ref && shouldProcess && !property.isEnum) { + importStatements.add(`const ${property.model} = require('./${property.model}');`); } }); return Array.from(importStatements).join('\n'); @@ -332,39 +288,6 @@ js.process = ({spec, operations, models, handlebars}) => { }[swaggerType] || swaggerType; }); - handlebars.registerHelper('modelMethodPublicArgumentTypeScriptTypingBuilder', (method, modelName) => { - const args = []; - - const operation = method.operation; - - operation.pathParams.forEach(param => { - const matchingArgument = method.arguments.filter(argument => argument.dest === param.name)[0]; - if (!matchingArgument || !matchingArgument.src){ - args.push(`${param.name}: ${param.type}`); - } - }); - - if ((operation.method === 'post' || operation.method === 'put') && operation.bodyModel && (operation.bodyModel !== modelName)) { - args.push(`${_.camelCase(operation.bodyModel)}: ${operation.bodyModel}`); - } - - if (operation.queryParams.length) { - args.push('queryParameters: object'); - } - - let returnType = 'undefined'; - if (operation.responseModel) { - if (operation.isArray) { - returnType = 'Promise'; - } else { - returnType = `Promise<${operation.responseModel}>`; - } - } - - const output = `(${args.join(', ')}): ${returnType};`; - return output; - }); - handlebars.registerHelper('getAffectedResources', (path) => { const resources = []; let pl = path.length; diff --git a/templates/model.d.ts.hbs b/templates/model.d.ts.hbs index f3b5d253a..9b18ca7eb 100644 --- a/templates/model.d.ts.hbs +++ b/templates/model.d.ts.hbs @@ -18,7 +18,7 @@ import {{extends}} from './{{extends}}'; {{else}} import Resource from '../../resource'; {{/if}} -{{{modelImportBuilder this 'ts'}}} +{{{typeScriptModelImportBuilder this}}} {{#if extends}} declare class {{modelName}} extends {{extends}} { @@ -39,14 +39,14 @@ declare class {{modelName}} extends Resource { {{#each crud}} {{#if (eq alias 'update')}} - {{alias}}{{{modelMethodPublicArgumentTypeScriptTypingBuilder this ../modelName}}} + {{alias}}{{{typeScriptModelMethodSignatureBuilder this ../modelName}}} {{/if}} {{#if (eq alias 'delete')}} - {{alias}}{{{modelMethodPublicArgumentTypeScriptTypingBuilder this ../modelName}}} + {{alias}}{{{typeScriptModelMethodSignatureBuilder this ../modelName}}} {{/if}} {{/each}} {{#each methods}} - {{alias}}{{{modelMethodPublicArgumentTypeScriptTypingBuilder this ../modelName}}} + {{alias}}{{{typeScriptModelMethodSignatureBuilder this ../modelName}}} {{/each}} } From 57daae0326d2f2600afac1637d372fb6af676685 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Mon, 15 Feb 2021 20:25:29 +0200 Subject: [PATCH 06/20] extract output formatting code into helper functions --- templates/helpers/operation.js | 135 +++++++++++++++------------------ 1 file changed, 61 insertions(+), 74 deletions(-) diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index 04d974342..757514815 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -132,6 +132,52 @@ const jsdocBuilder = (operation) => { return lines.join('\n'); }; +const typeScriptOperationSignatureBuilder = operation => { + const [args, returnType] = getOperationArgumentsAndReturnType(operation); + return `${operation.operationId}(${formatArguments(args).join(', ')}): ${formatReturnType(returnType)};`; +}; + +const typeScriptModelMethodSignatureBuilder = (method, modelName) => { + const [args, returnType] = getModelMethodArgumentsAndReturnType(method, modelName); + return `(${formatArguments(args).join(', ')}): ${formatReturnType(returnType)};`; +}; + +const typeScriptClientImportBuilder = operations => { + const operationsImportTypes = operations.reduce((acc, operation) => { + const [args, returnType] = getOperationArgumentsAndReturnType(operation); + return [ + ...acc, + ...args.values(), + returnType, + ] + }, []); + + const importStatements = formatImportStatements(operationsImportTypes); + return importStatements.join('\n'); +}; + +const typeScriptModelImportBuilder = model => { + const {properties, methods} = model; + if (!properties && !methods) { + return; + } + + const methodsImportTypes = model.methods.reduce((acc, method) => { + const [args, returnType] = getModelMethodArgumentsAndReturnType(method, model.modelName); + return [ + ...acc, + ...args.values(), + returnType, + ] + }, []); + + const propertiesImportTypes = + properties.filter(property => property.$ref).map(property => property.model); + + const importStatements = formatImportStatements(new Set([...methodsImportTypes, ...propertiesImportTypes])); + return importStatements.join('\n'); +}; + const getOperationArgumentsAndReturnType = operation => { const { bodyModel, method, pathParams, queryParams } = operation; const args = new Map(); @@ -184,18 +230,9 @@ const getModelMethodArgumentsAndReturnType = (method, modelName) => { return [args, returnType]; }; -const formatObjectLiteralType = typeProps => { - let objectLiteralType = '{ \n'; - typeProps.forEach(prop => { - objectLiteralType += ` ${prop}: string,\n` - }) - objectLiteralType += ' }'; - return objectLiteralType; -} - -const typeScriptOperationSignatureBuilder = (operation) => { - const [args, returnType] = getOperationArgumentsAndReturnType(operation); +const formatReturnType = returnType => returnType === 'undefined' ? 'undefined' : `Promise<${returnType}>`; +const formatArguments = args => { const typedArgs = []; for (let [arg, argType] of args) { if (Array.isArray(argType)) { @@ -204,52 +241,19 @@ const typeScriptOperationSignatureBuilder = (operation) => { typedArgs.push(`${arg}: ${argType}`) } } + return typedArgs; +} - return `${operation.operationId}(${typedArgs.join(', ')}): ${returnType === 'undefined' ? 'undefined' : `Promise<${returnType}>;`}`; -}; - -const typeScriptClientImportBuilder = (operations) => { - const operationsImportTypes = operations.reduce((acc, operation) => { - const [args, returnType] = getOperationArgumentsAndReturnType(operation); - return [ - ...acc, - ...args.values(), - returnType, - ] - }, []); - - const importStatements = []; - operationsImportTypes.forEach(type => { - if (!MODELS_SHOULD_NOT_PROCESS.includes(type) && !Array.isArray(type)) { - if (type === 'Collection') { - importStatements.push(`import Collection from '../collection';`); - } else { - importStatements.push(`import ${type} from './${type}';`); - } - } - }); - return importStatements.join('\n'); -}; - -const typeScriptModelImportBuilder = model => { - const {properties, methods} = model; - if (!properties && !methods) { - return; - } - - const methodsImportTypes = model.methods.reduce((acc, method) => { - const [args, returnType] = getModelMethodArgumentsAndReturnType(method, model.modelName); - return [ - ...acc, - ...args.values(), - returnType, - ] - }, []); - - const propertiesImportTypes = - properties.filter(property => property.$ref).map(property => property.model); +const formatObjectLiteralType = typeProps => { + let objectLiteralType = '{ \n'; + typeProps.forEach(prop => { + objectLiteralType += ` ${prop}: string,\n` + }) + objectLiteralType += ' }'; + return objectLiteralType; +} - const importTypes = new Set([...methodsImportTypes, ...propertiesImportTypes]); +const formatImportStatements = importTypes => { const importStatements = []; importTypes.forEach(type => { if (!MODELS_SHOULD_NOT_PROCESS.includes(type) && !Array.isArray(type)) { @@ -260,25 +264,8 @@ const typeScriptModelImportBuilder = model => { } } }); - return importStatements.join('\n'); -}; - - -const typeScriptModelMethodSignatureBuilder = (method, modelName) => { - console.log(method.operation.operationId) - const [args, returnType] = getModelMethodArgumentsAndReturnType(method, modelName); - - const typedArgs = []; - for (let [arg, argType] of args) { - if (Array.isArray(argType)) { - typedArgs.push(`${arg}: ${formatObjectLiteralType(argType)}`) - } else { - typedArgs.push(`${arg}: ${argType}`) - } - } - - return `(${typedArgs.join(', ')}): ${returnType === 'undefined' ? 'undefined' : `Promise<${returnType}>;`}`; -}; + return importStatements; +} module.exports = { From 1d2950e5a4375b80e7876c872da928183d822747 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Tue, 16 Feb 2021 11:51:20 +0200 Subject: [PATCH 07/20] move ts formatters to helpers, differentiate import path and return Promise --- templates/helpers/operation.js | 49 ++++++++++++++++++++++++++++------ templates/index.js | 28 ------------------- templates/model.d.ts.hbs | 2 +- 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index 757514815..75c946fa4 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -134,12 +134,12 @@ const jsdocBuilder = (operation) => { const typeScriptOperationSignatureBuilder = operation => { const [args, returnType] = getOperationArgumentsAndReturnType(operation); - return `${operation.operationId}(${formatArguments(args).join(', ')}): ${formatReturnType(returnType)};`; + return `${operation.operationId}(${formatArguments(args).join(', ')}): Promise<${returnType}>;`; }; const typeScriptModelMethodSignatureBuilder = (method, modelName) => { const [args, returnType] = getModelMethodArgumentsAndReturnType(method, modelName); - return `(${formatArguments(args).join(', ')}): ${formatReturnType(returnType)};`; + return `(${formatArguments(args).join(', ')}): Promise<${returnType}>;`; }; const typeScriptClientImportBuilder = operations => { @@ -152,7 +152,9 @@ const typeScriptClientImportBuilder = operations => { ] }, []); - const importStatements = formatImportStatements(operationsImportTypes); + const importStatements = formatImportStatements(new Set([...operationsImportTypes]), { + isModelToModelImport: false + }); return importStatements.join('\n'); }; @@ -230,8 +232,6 @@ const getModelMethodArgumentsAndReturnType = (method, modelName) => { return [args, returnType]; }; -const formatReturnType = returnType => returnType === 'undefined' ? 'undefined' : `Promise<${returnType}>`; - const formatArguments = args => { const typedArgs = []; for (let [arg, argType] of args) { @@ -253,20 +253,51 @@ const formatObjectLiteralType = typeProps => { return objectLiteralType; } -const formatImportStatements = importTypes => { +const formatImportStatements = (importTypes, formattingOptions = { + isModelToModelImport: true +}) => { const importStatements = []; + const { isModelToModelImport } = formattingOptions; importTypes.forEach(type => { if (!MODELS_SHOULD_NOT_PROCESS.includes(type) && !Array.isArray(type)) { if (type === 'Collection') { - importStatements.push(`import Collection from '../collection';`); + importStatements.push(`import Collection from '${isModelToModelImport ? '..' : '.'}/collection';`); } else { - importStatements.push(`import ${type} from './${type}';`); + importStatements.push(`import ${type} from '${isModelToModelImport ? './' : './models/'}${type}';`); } } }); return importStatements; } +const convertSwaggerToTSType = swaggerType => { + return { + array: '[]', + integer: 'number', + hash: '{\n\ [name: string]: unknown;\n\ }', + dateTime: 'string', + password: 'string', + }[swaggerType] || swaggerType; +}; + +const sanitizeModelPropertyName = propertyName => { + const restrictedChars = ['#']; + const knownConflictingPropertyNames = ['verify']; + let sanitizedPropertyName = propertyName; + + const containsRestrictedChars = restrictedChars.find(char => propertyName.includes(char)); + + if (knownConflictingPropertyNames.includes(propertyName)) { + sanitizedPropertyName = `_${propertyName}`; + } + + if (containsRestrictedChars) { + sanitizedPropertyName = `'${propertyName}'`; + } + + return sanitizedPropertyName; +}; + module.exports = { getBodyModelNameInCamelCase, @@ -281,4 +312,6 @@ module.exports = { typeScriptModelImportBuilder, typeScriptModelMethodSignatureBuilder, typeScriptClientImportBuilder, + convertSwaggerToTSType, + sanitizeModelPropertyName } diff --git a/templates/index.js b/templates/index.js index 3f3453174..16b191855 100644 --- a/templates/index.js +++ b/templates/index.js @@ -131,24 +131,6 @@ js.process = ({spec, operations, models, handlebars}) => { // Register Operation helpers Object.keys(operationUtils).forEach(key => handlebars.registerHelper(key, operationUtils[key])); - handlebars.registerHelper('sanitizeModelPropertyName', propertyName => { - const restrictedChars = ['#']; - const knownConflictingPropertyNames = ['verify']; - let sanitizedPropertyName = propertyName; - - const containsRestrictedChars = restrictedChars.find(char => propertyName.includes(char)); - - if (knownConflictingPropertyNames.includes(propertyName)) { - sanitizedPropertyName = `_${propertyName}`; - } - - if (containsRestrictedChars) { - sanitizedPropertyName = `'${propertyName}'`; - } - - return sanitizedPropertyName; - }); - // TODO: move helpers to modules const paramMatcher = /{(.*?)}/g; handlebars.registerHelper('replacePathParams', (path) => { @@ -278,16 +260,6 @@ js.process = ({spec, operations, models, handlebars}) => { return output; }); - handlebars.registerHelper('convertSwaggerToTSType', (swaggerType) => { - return { - array: '[]', - integer: 'number', - hash: '{\n\ [name: string]: unknown;\n\ }', - dateTime: 'string', - password: 'string', - }[swaggerType] || swaggerType; - }); - handlebars.registerHelper('getAffectedResources', (path) => { const resources = []; let pl = path.length; diff --git a/templates/model.d.ts.hbs b/templates/model.d.ts.hbs index 9b18ca7eb..45b1bd940 100644 --- a/templates/model.d.ts.hbs +++ b/templates/model.d.ts.hbs @@ -16,7 +16,7 @@ {{#if extends~}} import {{extends}} from './{{extends}}'; {{else}} -import Resource from '../../resource'; +import Resource from '../resource'; {{/if}} {{{typeScriptModelImportBuilder this}}} From 25765789d1e93137fd60a8ead96202811ca7d8c5 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Tue, 16 Feb 2021 12:31:55 +0200 Subject: [PATCH 08/20] restrict self import for models, add conversion from double --- templates/helpers/operation.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index 75c946fa4..1059b4204 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -155,7 +155,7 @@ const typeScriptClientImportBuilder = operations => { const importStatements = formatImportStatements(new Set([...operationsImportTypes]), { isModelToModelImport: false }); - return importStatements.join('\n'); + return importStatements; }; const typeScriptModelImportBuilder = model => { @@ -176,8 +176,9 @@ const typeScriptModelImportBuilder = model => { const propertiesImportTypes = properties.filter(property => property.$ref).map(property => property.model); - const importStatements = formatImportStatements(new Set([...methodsImportTypes, ...propertiesImportTypes])); - return importStatements.join('\n'); + const uniqueImportTypes = new Set([...methodsImportTypes, ...propertiesImportTypes]); + uniqueImportTypes.delete(model.modelName); + return formatImportStatements(uniqueImportTypes); }; const getOperationArgumentsAndReturnType = operation => { @@ -267,13 +268,14 @@ const formatImportStatements = (importTypes, formattingOptions = { } } }); - return importStatements; + return importStatements.join('/n'); } const convertSwaggerToTSType = swaggerType => { return { array: '[]', integer: 'number', + double: 'number', hash: '{\n\ [name: string]: unknown;\n\ }', dateTime: 'string', password: 'string', From d804c8ce0b8167c702817f8b186c77c1c739a0aa Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Tue, 16 Feb 2021 14:38:10 +0200 Subject: [PATCH 09/20] correct newline syntax --- templates/helpers/operation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index 1059b4204..8f313087e 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -268,7 +268,7 @@ const formatImportStatements = (importTypes, formattingOptions = { } } }); - return importStatements.join('/n'); + return importStatements.join('\n'); } const convertSwaggerToTSType = swaggerType => { From d5261439368a08997edd8371a1fba41a9ce5b86b Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Tue, 16 Feb 2021 14:50:44 +0200 Subject: [PATCH 10/20] update copyright year --- templates/index.js | 3 ++- templates/model.d.ts.hbs | 2 +- templates/model.index.d.ts.hbs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/templates/index.js b/templates/index.js index 16b191855..824c5c8c2 100644 --- a/templates/index.js +++ b/templates/index.js @@ -145,7 +145,7 @@ js.process = ({spec, operations, models, handlebars}) => { return new handlebars.SafeString(path); }); - handlebars.registerHelper('modelImportBuilder', model => { + handlebars.registerHelper('modelImportBuilder', (model) => { if (!model.properties) { return; } @@ -177,6 +177,7 @@ js.process = ({spec, operations, models, handlebars}) => { handlebars.registerHelper('modelMethodPublicArgumentBuilder', (method, modelName) => { const args = []; + const operation = method.operation; operation.pathParams.forEach(param => { diff --git a/templates/model.d.ts.hbs b/templates/model.d.ts.hbs index 45b1bd940..4c8b4258e 100644 --- a/templates/model.d.ts.hbs +++ b/templates/model.d.ts.hbs @@ -1,5 +1,5 @@ /*! - * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. + * Copyright (c) 2017-2021, Okta, Inc. and/or its affiliates. All rights reserved. * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") * * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. diff --git a/templates/model.index.d.ts.hbs b/templates/model.index.d.ts.hbs index 0455bf2cb..246ba6d9a 100644 --- a/templates/model.index.d.ts.hbs +++ b/templates/model.index.d.ts.hbs @@ -1,5 +1,5 @@ /*! - * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. + * Copyright (c) 2017-2021, Okta, Inc. and/or its affiliates. All rights reserved. * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") * * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. From c8e378f9acb0e9c9b196720411ecc68e2c8d5c50 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Tue, 16 Feb 2021 14:56:41 +0200 Subject: [PATCH 11/20] add typings for Collection, Resource, Request --- src/types/collection.d.ts | 26 ++++++++++++++++++++++++++ src/types/request.d.ts | 5 +++++ src/types/resource.d.ts | 16 ++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/types/collection.d.ts create mode 100644 src/types/request.d.ts create mode 100644 src/types/resource.d.ts diff --git a/src/types/collection.d.ts b/src/types/collection.d.ts new file mode 100644 index 000000000..ed8ac1e95 --- /dev/null +++ b/src/types/collection.d.ts @@ -0,0 +1,26 @@ +export = Collection; + +import Request from './request'; + +declare class Collection { + constructor(client: any, uri: string, factory: any, request?: Request); + nextUri: string; + client: any; + factory: any; + currentItems: any[]; + request: Request; + next(): Promise; + [Symbol.asyncIterator](): { + next: () => Promise; + }; + getNextPage(): any; + each(iterator: Function): any; + subscribe(config: { + interval: number; + next: Function; + error: Function; + complete: Function; + }): { + unsubscribe(): void; + }; +} diff --git a/src/types/request.d.ts b/src/types/request.d.ts new file mode 100644 index 000000000..9dd1760e9 --- /dev/null +++ b/src/types/request.d.ts @@ -0,0 +1,5 @@ +declare interface Request { + api(url: string): Promise; +} + +export default Request; \ No newline at end of file diff --git a/src/types/resource.d.ts b/src/types/resource.d.ts new file mode 100644 index 000000000..c30d01b55 --- /dev/null +++ b/src/types/resource.d.ts @@ -0,0 +1,16 @@ +export = Resource; +/*! + * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. + * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and limitations under the License. + */ + +declare class Resource { + constructor(resourceJson: any, client: any); +} From 70619d51b497fa3af5d0132ae247ad9d2a0cf0da Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Tue, 16 Feb 2021 14:59:33 +0200 Subject: [PATCH 12/20] push latest generated-client --- src/types/generated-client.d.ts | 240 ++++++++++++++++---------------- 1 file changed, 120 insertions(+), 120 deletions(-) diff --git a/src/types/generated-client.d.ts b/src/types/generated-client.d.ts index 40022218b..9ce232343 100644 --- a/src/types/generated-client.d.ts +++ b/src/types/generated-client.d.ts @@ -10,54 +10,54 @@ * See the License for the specific language governing permissions and limitations under the License. */ -import Collection from '../collection'; -import Application from'./models/Application'; -import CsrMetadata from'./models/CsrMetadata'; -import Csr from'./models/Csr'; -import JsonWebKey from'./models/JsonWebKey'; -import OAuth2ScopeConsentGrant from'./models/OAuth2ScopeConsentGrant'; -import ApplicationGroupAssignment from'./models/ApplicationGroupAssignment'; -import OAuth2Token from'./models/OAuth2Token'; -import AppUser from'./models/AppUser'; -import AuthorizationServer from'./models/AuthorizationServer'; -import OAuth2Claim from'./models/OAuth2Claim'; -import OAuth2RefreshToken from'./models/OAuth2RefreshToken'; -import JwkUse from'./models/JwkUse'; -import AuthorizationServerPolicy from'./models/AuthorizationServerPolicy'; -import AuthorizationServerPolicyRule from'./models/AuthorizationServerPolicyRule'; -import OAuth2Scope from'./models/OAuth2Scope'; -import EventHook from'./models/EventHook'; -import Feature from'./models/Feature'; -import Group from'./models/Group'; -import GroupRule from'./models/GroupRule'; -import AssignRoleRequest from'./models/AssignRoleRequest'; -import Role from'./models/Role'; -import IdentityProvider from'./models/IdentityProvider'; -import IdentityProviderApplicationUser from'./models/IdentityProviderApplicationUser'; -import UserIdentityProviderLinkRequest from'./models/UserIdentityProviderLinkRequest'; -import InlineHook from'./models/InlineHook'; -import InlineHookPayload from'./models/InlineHookPayload'; -import InlineHookResponse from'./models/InlineHookResponse'; -import LinkedObject from'./models/LinkedObject'; -import UserType from'./models/UserType'; -import Policy from'./models/Policy'; -import PolicyRule from'./models/PolicyRule'; -import CreateSessionRequest from'./models/CreateSessionRequest'; -import Session from'./models/Session'; -import SmsTemplate from'./models/SmsTemplate'; -import TrustedOrigin from'./models/TrustedOrigin'; -import CreateUserRequest from'./models/CreateUserRequest'; -import User from'./models/User'; -import ChangePasswordRequest from'./models/ChangePasswordRequest'; -import UserCredentials from'./models/UserCredentials'; -import ForgotPasswordResponse from'./models/ForgotPasswordResponse'; -import UserFactor from'./models/UserFactor'; -import ActivateFactorRequest from'./models/ActivateFactorRequest'; -import VerifyUserFactorResponse from'./models/VerifyUserFactorResponse'; -import VerifyFactorRequest from'./models/VerifyFactorRequest'; -import UserActivationToken from'./models/UserActivationToken'; -import TempPassword from'./models/TempPassword'; -import ResetPasswordToken from'./models/ResetPasswordToken'; +import Collection from './collection'; +import Application from './models/Application'; +import CsrMetadata from './models/CsrMetadata'; +import Csr from './models/Csr'; +import JsonWebKey from './models/JsonWebKey'; +import OAuth2ScopeConsentGrant from './models/OAuth2ScopeConsentGrant'; +import ApplicationGroupAssignment from './models/ApplicationGroupAssignment'; +import OAuth2Token from './models/OAuth2Token'; +import AppUser from './models/AppUser'; +import AuthorizationServer from './models/AuthorizationServer'; +import OAuth2Claim from './models/OAuth2Claim'; +import OAuth2RefreshToken from './models/OAuth2RefreshToken'; +import JwkUse from './models/JwkUse'; +import AuthorizationServerPolicy from './models/AuthorizationServerPolicy'; +import AuthorizationServerPolicyRule from './models/AuthorizationServerPolicyRule'; +import OAuth2Scope from './models/OAuth2Scope'; +import EventHook from './models/EventHook'; +import Feature from './models/Feature'; +import Group from './models/Group'; +import GroupRule from './models/GroupRule'; +import AssignRoleRequest from './models/AssignRoleRequest'; +import Role from './models/Role'; +import IdentityProvider from './models/IdentityProvider'; +import IdentityProviderApplicationUser from './models/IdentityProviderApplicationUser'; +import UserIdentityProviderLinkRequest from './models/UserIdentityProviderLinkRequest'; +import InlineHook from './models/InlineHook'; +import InlineHookPayload from './models/InlineHookPayload'; +import InlineHookResponse from './models/InlineHookResponse'; +import LinkedObject from './models/LinkedObject'; +import UserType from './models/UserType'; +import Policy from './models/Policy'; +import PolicyRule from './models/PolicyRule'; +import CreateSessionRequest from './models/CreateSessionRequest'; +import Session from './models/Session'; +import SmsTemplate from './models/SmsTemplate'; +import TrustedOrigin from './models/TrustedOrigin'; +import CreateUserRequest from './models/CreateUserRequest'; +import User from './models/User'; +import ChangePasswordRequest from './models/ChangePasswordRequest'; +import UserCredentials from './models/UserCredentials'; +import ForgotPasswordResponse from './models/ForgotPasswordResponse'; +import UserFactor from './models/UserFactor'; +import ActivateFactorRequest from './models/ActivateFactorRequest'; +import VerifyUserFactorResponse from './models/VerifyUserFactorResponse'; +import VerifyFactorRequest from './models/VerifyFactorRequest'; +import UserActivationToken from './models/UserActivationToken'; +import TempPassword from './models/TempPassword'; +import ResetPasswordToken from './models/ResetPasswordToken'; declare class GeneratedApiClient { listApplications(queryParameters: { @@ -71,14 +71,14 @@ declare class GeneratedApiClient { createApplication(application: Application, queryParameters: { activate: string, }): Promise; - deleteApplication(appId: string): undefined; + deleteApplication(appId: string): Promise; getApplication(appId: string, queryParameters: { expand: string, }): Promise; updateApplication(appId: string, application: Application): Promise; listCsrsForApplication(appId: string): Promise; generateCsrForApplication(appId: string, csrMetadata: CsrMetadata): Promise; - revokeCsrFromApplication(appId: string, csrId: string): undefined; + revokeCsrFromApplication(appId: string, csrId: string): Promise; getCsrForApplication(appId: string, csrId: string): Promise; publishCerCert(appId: string, csrId: string, certificate: string): Promise; publishBinaryCerCert(appId: string, csrId: string, certificate: string): Promise; @@ -97,7 +97,7 @@ declare class GeneratedApiClient { expand: string, }): Promise; grantConsentToScope(appId: string, oAuth2ScopeConsentGrant: OAuth2ScopeConsentGrant): Promise; - revokeScopeConsentGrant(appId: string, grantId: string): undefined; + revokeScopeConsentGrant(appId: string, grantId: string): Promise; getScopeConsentGrant(appId: string, grantId: string, queryParameters: { expand: string, }): Promise; @@ -107,20 +107,20 @@ declare class GeneratedApiClient { limit: string, expand: string, }): Promise; - deleteApplicationGroupAssignment(appId: string, groupId: string): undefined; + deleteApplicationGroupAssignment(appId: string, groupId: string): Promise; getApplicationGroupAssignment(appId: string, groupId: string, queryParameters: { expand: string, }): Promise; createApplicationGroupAssignment(appId: string, groupId: string, applicationGroupAssignment: ApplicationGroupAssignment): Promise; - activateApplication(appId: string): undefined; - deactivateApplication(appId: string): undefined; - revokeOAuth2TokensForApplication(appId: string): undefined; + activateApplication(appId: string): Promise; + deactivateApplication(appId: string): Promise; + revokeOAuth2TokensForApplication(appId: string): Promise; listOAuth2TokensForApplication(appId: string, queryParameters: { expand: string, after: string, limit: string, }): Promise; - revokeOAuth2TokenForApplication(appId: string, tokenId: string): undefined; + revokeOAuth2TokenForApplication(appId: string, tokenId: string): Promise; getOAuth2TokenForApplication(appId: string, tokenId: string, queryParameters: { expand: string, }): Promise; @@ -135,7 +135,7 @@ declare class GeneratedApiClient { assignUserToApplication(appId: string, appUser: AppUser): Promise; deleteApplicationUser(appId: string, userId: string, queryParameters: { sendEmail: string, - }): undefined; + }): Promise; getApplicationUser(appId: string, userId: string, queryParameters: { expand: string, }): Promise; @@ -146,37 +146,37 @@ declare class GeneratedApiClient { after: string, }): Promise; createAuthorizationServer(authorizationServer: AuthorizationServer): Promise; - deleteAuthorizationServer(authServerId: string): undefined; + deleteAuthorizationServer(authServerId: string): Promise; getAuthorizationServer(authServerId: string): Promise; updateAuthorizationServer(authServerId: string, authorizationServer: AuthorizationServer): Promise; listOAuth2Claims(authServerId: string): Promise; createOAuth2Claim(authServerId: string, oAuth2Claim: OAuth2Claim): Promise; - deleteOAuth2Claim(authServerId: string, claimId: string): undefined; + deleteOAuth2Claim(authServerId: string, claimId: string): Promise; getOAuth2Claim(authServerId: string, claimId: string): Promise; updateOAuth2Claim(authServerId: string, claimId: string, oAuth2Claim: OAuth2Claim): Promise; listOAuth2ClientsForAuthorizationServer(authServerId: string): Promise; - revokeRefreshTokensForAuthorizationServerAndClient(authServerId: string, clientId: string): undefined; + revokeRefreshTokensForAuthorizationServerAndClient(authServerId: string, clientId: string): Promise; listRefreshTokensForAuthorizationServerAndClient(authServerId: string, clientId: string, queryParameters: { expand: string, after: string, limit: string, }): Promise; - revokeRefreshTokenForAuthorizationServerAndClient(authServerId: string, clientId: string, tokenId: string): undefined; + revokeRefreshTokenForAuthorizationServerAndClient(authServerId: string, clientId: string, tokenId: string): Promise; getRefreshTokenForAuthorizationServerAndClient(authServerId: string, clientId: string, tokenId: string, queryParameters: { expand: string, }): Promise; listAuthorizationServerKeys(authServerId: string): Promise; - rotateAuthorizationServerKeys(authServerId: string): Promise; - activateAuthorizationServer(authServerId: string): undefined; - deactivateAuthorizationServer(authServerId: string): undefined; + rotateAuthorizationServerKeys(authServerId: string, jwkUse: JwkUse): Promise; + activateAuthorizationServer(authServerId: string): Promise; + deactivateAuthorizationServer(authServerId: string): Promise; listAuthorizationServerPolicies(authServerId: string): Promise; createAuthorizationServerPolicy(authServerId: string, authorizationServerPolicy: AuthorizationServerPolicy): Promise; - deleteAuthorizationServerPolicy(authServerId: string, policyId: string): undefined; + deleteAuthorizationServerPolicy(authServerId: string, policyId: string): Promise; getAuthorizationServerPolicy(authServerId: string, policyId: string): Promise; updateAuthorizationServerPolicy(authServerId: string, policyId: string, authorizationServerPolicy: AuthorizationServerPolicy): Promise; listAuthorizationServerPolicyRules(policyId: string, authServerId: string): Promise; createAuthorizationServerPolicyRule(policyId: string, authServerId: string, authorizationServerPolicyRule: AuthorizationServerPolicyRule): Promise; - deleteAuthorizationServerPolicyRule(policyId: string, authServerId: string, ruleId: string): undefined; + deleteAuthorizationServerPolicyRule(policyId: string, authServerId: string, ruleId: string): Promise; getAuthorizationServerPolicyRule(policyId: string, authServerId: string, ruleId: string): Promise; updateAuthorizationServerPolicyRule(policyId: string, authServerId: string, ruleId: string, authorizationServerPolicyRule: AuthorizationServerPolicyRule): Promise; listOAuth2Scopes(authServerId: string, queryParameters: { @@ -186,12 +186,12 @@ declare class GeneratedApiClient { limit: string, }): Promise; createOAuth2Scope(authServerId: string, oAuth2Scope: OAuth2Scope): Promise; - deleteOAuth2Scope(authServerId: string, scopeId: string): undefined; + deleteOAuth2Scope(authServerId: string, scopeId: string): Promise; getOAuth2Scope(authServerId: string, scopeId: string): Promise; updateOAuth2Scope(authServerId: string, scopeId: string, oAuth2Scope: OAuth2Scope): Promise; listEventHooks(): Promise; createEventHook(eventHook: EventHook): Promise; - deleteEventHook(eventHookId: string): undefined; + deleteEventHook(eventHookId: string): Promise; getEventHook(eventHookId: string): Promise; updateEventHook(eventHookId: string, eventHook: EventHook): Promise; activateEventHook(eventHookId: string): Promise; @@ -219,14 +219,14 @@ declare class GeneratedApiClient { expand: string, }): Promise; createGroupRule(groupRule: GroupRule): Promise; - deleteGroupRule(ruleId: string): undefined; + deleteGroupRule(ruleId: string): Promise; getGroupRule(ruleId: string, queryParameters: { expand: string, }): Promise; updateGroupRule(ruleId: string, groupRule: GroupRule): Promise; - activateGroupRule(ruleId: string): undefined; - deactivateGroupRule(ruleId: string): undefined; - deleteGroup(groupId: string): undefined; + activateGroupRule(ruleId: string): Promise; + deactivateGroupRule(ruleId: string): Promise; + deleteGroup(groupId: string): Promise; getGroup(groupId: string): Promise; updateGroup(groupId: string, group: Group): Promise; listAssignedApplicationsForGroup(groupId: string, queryParameters: { @@ -239,28 +239,28 @@ declare class GeneratedApiClient { assignRoleToGroup(groupId: string, assignRoleRequest: AssignRoleRequest, queryParameters: { disableNotifications: string, }): Promise; - removeRoleFromGroup(groupId: string, roleId: string): undefined; + removeRoleFromGroup(groupId: string, roleId: string): Promise; getRole(groupId: string, roleId: string): Promise; listApplicationTargetsForApplicationAdministratorRoleForGroup(groupId: string, roleId: string, queryParameters: { after: string, limit: string, }): Promise; - removeApplicationTargetFromApplicationAdministratorRoleGivenToGroup(groupId: string, roleId: string, appName: string): undefined; - addApplicationTargetToAdminRoleGivenToGroup(groupId: string, roleId: string, appName: string): undefined; - removeApplicationTargetFromAdministratorRoleGivenToGroup(groupId: string, roleId: string, appName: string, applicationId: string): undefined; - addApplicationInstanceTargetToAppAdminRoleGivenToGroup(groupId: string, roleId: string, appName: string, applicationId: string): undefined; + removeApplicationTargetFromApplicationAdministratorRoleGivenToGroup(groupId: string, roleId: string, appName: string): Promise; + addApplicationTargetToAdminRoleGivenToGroup(groupId: string, roleId: string, appName: string): Promise; + removeApplicationTargetFromAdministratorRoleGivenToGroup(groupId: string, roleId: string, appName: string, applicationId: string): Promise; + addApplicationInstanceTargetToAppAdminRoleGivenToGroup(groupId: string, roleId: string, appName: string, applicationId: string): Promise; listGroupTargetsForGroupRole(groupId: string, roleId: string, queryParameters: { after: string, limit: string, }): Promise; - removeGroupTargetFromGroupAdministratorRoleGivenToGroup(groupId: string, roleId: string, targetGroupId: string): undefined; - addGroupTargetToGroupAdministratorRoleForGroup(groupId: string, roleId: string, targetGroupId: string): undefined; + removeGroupTargetFromGroupAdministratorRoleGivenToGroup(groupId: string, roleId: string, targetGroupId: string): Promise; + addGroupTargetToGroupAdministratorRoleForGroup(groupId: string, roleId: string, targetGroupId: string): Promise; listGroupUsers(groupId: string, queryParameters: { after: string, limit: string, }): Promise; - removeUserFromGroup(groupId: string, userId: string): undefined; - addUserToGroup(groupId: string, userId: string): undefined; + removeUserFromGroup(groupId: string, userId: string): Promise; + addUserToGroup(groupId: string, userId: string): Promise; listIdentityProviders(queryParameters: { q: string, after: string, @@ -273,14 +273,14 @@ declare class GeneratedApiClient { limit: string, }): Promise; createIdentityProviderKey(jsonWebKey: JsonWebKey): Promise; - deleteIdentityProviderKey(keyId: string): undefined; + deleteIdentityProviderKey(keyId: string): Promise; getIdentityProviderKey(keyId: string): Promise; - deleteIdentityProvider(idpId: string): undefined; + deleteIdentityProvider(idpId: string): Promise; getIdentityProvider(idpId: string): Promise; updateIdentityProvider(idpId: string, identityProvider: IdentityProvider): Promise; listCsrsForIdentityProvider(idpId: string): Promise; generateCsrForIdentityProvider(idpId: string, csrMetadata: CsrMetadata): Promise; - revokeCsrForIdentityProvider(idpId: string, csrId: string): undefined; + revokeCsrForIdentityProvider(idpId: string, csrId: string): Promise; getCsrForIdentityProvider(idpId: string, csrId: string): Promise; publishCerCertForIdentityProvider(idpId: string, csrId: string, certificate: string): Promise; publishBinaryCerCertForIdentityProvider(idpId: string, csrId: string, certificate: string): Promise; @@ -298,7 +298,7 @@ declare class GeneratedApiClient { activateIdentityProvider(idpId: string): Promise; deactivateIdentityProvider(idpId: string): Promise; listIdentityProviderApplicationUsers(idpId: string): Promise; - unlinkUserFromIdentityProvider(idpId: string, userId: string): undefined; + unlinkUserFromIdentityProvider(idpId: string, userId: string): Promise; getIdentityProviderApplicationUser(idpId: string, userId: string): Promise; linkUserToIdentityProvider(idpId: string, userId: string, userIdentityProviderLinkRequest: UserIdentityProviderLinkRequest): Promise; listSocialAuthTokens(idpId: string, userId: string): Promise; @@ -306,7 +306,7 @@ declare class GeneratedApiClient { type: string, }): Promise; createInlineHook(inlineHook: InlineHook): Promise; - deleteInlineHook(inlineHookId: string): undefined; + deleteInlineHook(inlineHookId: string): Promise; getInlineHook(inlineHookId: string): Promise; updateInlineHook(inlineHookId: string, inlineHook: InlineHook): Promise; executeInlineHook(inlineHookId: string, inlineHookPayload: InlineHookPayload): Promise; @@ -323,11 +323,11 @@ declare class GeneratedApiClient { }): Promise; listLinkedObjectDefinitions(): Promise; addLinkedObjectDefinition(linkedObject: LinkedObject): Promise; - deleteLinkedObjectDefinition(linkedObjectName: string): undefined; + deleteLinkedObjectDefinition(linkedObjectName: string): Promise; getLinkedObjectDefinition(linkedObjectName: string): Promise; listUserTypes(): Promise; createUserType(userType: UserType): Promise; - deleteUserType(typeId: string): undefined; + deleteUserType(typeId: string): Promise; getUserType(typeId: string): Promise; updateUserType(typeId: string, userType: UserType): Promise; replaceUserType(typeId: string, userType: UserType): Promise; @@ -339,29 +339,29 @@ declare class GeneratedApiClient { createPolicy(policy: Policy, queryParameters: { activate: string, }): Promise; - deletePolicy(policyId: string): undefined; + deletePolicy(policyId: string): Promise; getPolicy(policyId: string, queryParameters: { expand: string, }): Promise; updatePolicy(policyId: string, policy: Policy): Promise; - activatePolicy(policyId: string): undefined; - deactivatePolicy(policyId: string): undefined; + activatePolicy(policyId: string): Promise; + deactivatePolicy(policyId: string): Promise; listPolicyRules(policyId: string): Promise; createPolicyRule(policyId: string, policyRule: PolicyRule): Promise; - deletePolicyRule(policyId: string, ruleId: string): undefined; + deletePolicyRule(policyId: string, ruleId: string): Promise; getPolicyRule(policyId: string, ruleId: string): Promise; updatePolicyRule(policyId: string, ruleId: string, policyRule: PolicyRule): Promise; - activatePolicyRule(policyId: string, ruleId: string): undefined; - deactivatePolicyRule(policyId: string, ruleId: string): undefined; + activatePolicyRule(policyId: string, ruleId: string): Promise; + deactivatePolicyRule(policyId: string, ruleId: string): Promise; createSession(createSessionRequest: CreateSessionRequest): Promise; - endSession(sessionId: string): undefined; + endSession(sessionId: string): Promise; getSession(sessionId: string): Promise; refreshSession(sessionId: string): Promise; listSmsTemplates(queryParameters: { templateType: string, }): Promise; createSmsTemplate(smsTemplate: SmsTemplate): Promise; - deleteSmsTemplate(templateId: string): undefined; + deleteSmsTemplate(templateId: string): Promise; getSmsTemplate(templateId: string): Promise; partialUpdateSmsTemplate(templateId: string, smsTemplate: SmsTemplate): Promise; updateSmsTemplate(templateId: string, smsTemplate: SmsTemplate): Promise; @@ -372,7 +372,7 @@ declare class GeneratedApiClient { limit: string, }): Promise; createOrigin(trustedOrigin: TrustedOrigin): Promise; - deleteOrigin(trustedOriginId: string): undefined; + deleteOrigin(trustedOriginId: string): Promise; getOrigin(trustedOriginId: string): Promise; updateOrigin(trustedOriginId: string, trustedOrigin: TrustedOrigin): Promise; activateOrigin(trustedOriginId: string): Promise; @@ -391,10 +391,10 @@ declare class GeneratedApiClient { provider: string, nextLogin: string, }): Promise; - setLinkedObjectForUser(associatedUserId: string, primaryRelationshipName: string, primaryUserId: string): undefined; + setLinkedObjectForUser(associatedUserId: string, primaryRelationshipName: string, primaryUserId: string): Promise; deactivateOrDeleteUser(userId: string, queryParameters: { sendEmail: string, - }): undefined; + }): Promise; getUser(userId: string): Promise; partialUpdateUser(userId: string, user: User, queryParameters: { strict: string, @@ -404,19 +404,19 @@ declare class GeneratedApiClient { }): Promise; listAppLinks(userId: string): Promise; listUserClients(userId: string): Promise; - revokeGrantsForUserAndClient(userId: string, clientId: string): undefined; + revokeGrantsForUserAndClient(userId: string, clientId: string): Promise; listGrantsForUserAndClient(userId: string, clientId: string, queryParameters: { expand: string, after: string, limit: string, }): Promise; - revokeTokensForUserAndClient(userId: string, clientId: string): undefined; + revokeTokensForUserAndClient(userId: string, clientId: string): Promise; listRefreshTokensForUserAndClient(userId: string, clientId: string, queryParameters: { expand: string, after: string, limit: string, }): Promise; - revokeTokenForUserAndClient(userId: string, clientId: string, tokenId: string): undefined; + revokeTokenForUserAndClient(userId: string, clientId: string, tokenId: string): Promise; getRefreshTokenForUserAndClient(userId: string, clientId: string, tokenId: string, queryParameters: { expand: string, limit: string, @@ -441,7 +441,7 @@ declare class GeneratedApiClient { }): Promise; listSupportedFactors(userId: string): Promise; listSupportedSecurityQuestions(userId: string): Promise; - deleteFactor(userId: string, factorId: string): undefined; + deleteFactor(userId: string, factorId: string): Promise; getFactor(userId: string, factorId: string): Promise; activateFactor(userId: string, factorId: string, activateFactorRequest: ActivateFactorRequest): Promise; getFactorTransactionStatus(userId: string, factorId: string, transactionId: string): Promise; @@ -449,14 +449,14 @@ declare class GeneratedApiClient { templateId: string, tokenLifetimeSeconds: string, }): Promise; - revokeUserGrants(userId: string): undefined; + revokeUserGrants(userId: string): Promise; listUserGrants(userId: string, queryParameters: { scopeId: string, expand: string, after: string, limit: string, }): Promise; - revokeUserGrant(userId: string, grantId: string): undefined; + revokeUserGrant(userId: string, grantId: string): Promise; getUserGrant(userId: string, grantId: string, queryParameters: { expand: string, }): Promise; @@ -467,20 +467,20 @@ declare class GeneratedApiClient { }): Promise; deactivateUser(userId: string, queryParameters: { sendEmail: string, - }): undefined; + }): Promise; expirePassword(userId: string): Promise; expirePasswordAndGetTemporaryPassword(userId: string): Promise; reactivateUser(userId: string, queryParameters: { sendEmail: string, }): Promise; - resetFactors(userId: string): undefined; + resetFactors(userId: string): Promise; resetPassword(userId: string, queryParameters: { sendEmail: string, }): Promise; - suspendUser(userId: string): undefined; - unlockUser(userId: string): undefined; - unsuspendUser(userId: string): undefined; - removeLinkedObjectForUser(userId: string, relationshipName: string): undefined; + suspendUser(userId: string): Promise; + unlockUser(userId: string): Promise; + unsuspendUser(userId: string): Promise; + removeLinkedObjectForUser(userId: string, relationshipName: string): Promise; getLinkedObjectsForUser(userId: string, relationshipName: string, queryParameters: { after: string, limit: string, @@ -491,25 +491,25 @@ declare class GeneratedApiClient { assignRoleToUser(userId: string, assignRoleRequest: AssignRoleRequest, queryParameters: { disableNotifications: string, }): Promise; - removeRoleFromUser(userId: string, roleId: string): undefined; + removeRoleFromUser(userId: string, roleId: string): Promise; listApplicationTargetsForApplicationAdministratorRoleForUser(userId: string, roleId: string, queryParameters: { after: string, limit: string, }): Promise; - addAllAppsAsTargetToRole(userId: string, roleId: string): undefined; - removeApplicationTargetFromApplicationAdministratorRoleForUser(userId: string, roleId: string, appName: string): undefined; - addApplicationTargetToAdminRoleForUser(userId: string, roleId: string, appName: string): undefined; - removeApplicationTargetFromAdministratorRoleForUser(userId: string, roleId: string, appName: string, applicationId: string): undefined; - addApplicationTargetToAppAdminRoleForUser(userId: string, roleId: string, appName: string, applicationId: string): undefined; + addAllAppsAsTargetToRole(userId: string, roleId: string): Promise; + removeApplicationTargetFromApplicationAdministratorRoleForUser(userId: string, roleId: string, appName: string): Promise; + addApplicationTargetToAdminRoleForUser(userId: string, roleId: string, appName: string): Promise; + removeApplicationTargetFromAdministratorRoleForUser(userId: string, roleId: string, appName: string, applicationId: string): Promise; + addApplicationTargetToAppAdminRoleForUser(userId: string, roleId: string, appName: string, applicationId: string): Promise; listGroupTargetsForRole(userId: string, roleId: string, queryParameters: { after: string, limit: string, }): Promise; - removeGroupTargetFromRole(userId: string, roleId: string, groupId: string): undefined; - addGroupTargetToRole(userId: string, roleId: string, groupId: string): undefined; + removeGroupTargetFromRole(userId: string, roleId: string, groupId: string): Promise; + addGroupTargetToRole(userId: string, roleId: string, groupId: string): Promise; clearUserSessions(userId: string, queryParameters: { oauthTokens: string, - }): undefined; + }): Promise; } export = GeneratedApiClient; From 34cfc197f86b52ec040f143b551cb074c43384d6 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Tue, 16 Feb 2021 16:00:03 +0200 Subject: [PATCH 13/20] add types index and remove unused templates --- src/types/collection.d.ts | 14 ++++++++++++- .../types/index.d.ts | 10 ++++----- src/types/request.d.ts | 14 ++++++++++++- src/types/resource.d.ts | 2 +- templates/factory.d.ts.hbs | 21 ------------------- 5 files changed, 31 insertions(+), 30 deletions(-) rename templates/factories.index.d.ts.hbs => src/types/index.d.ts (83%) delete mode 100644 templates/factory.d.ts.hbs diff --git a/src/types/collection.d.ts b/src/types/collection.d.ts index ed8ac1e95..94778aadf 100644 --- a/src/types/collection.d.ts +++ b/src/types/collection.d.ts @@ -1,4 +1,14 @@ -export = Collection; +/*! + * Copyright (c) 2017-2021, Okta, Inc. and/or its affiliates. All rights reserved. + * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and limitations under the License. + */ import Request from './request'; @@ -24,3 +34,5 @@ declare class Collection { unsubscribe(): void; }; } + +export default Collection; diff --git a/templates/factories.index.d.ts.hbs b/src/types/index.d.ts similarity index 83% rename from templates/factories.index.d.ts.hbs rename to src/types/index.d.ts index 349ef4087..196e5c85f 100644 --- a/templates/factories.index.d.ts.hbs +++ b/src/types/index.d.ts @@ -10,10 +10,8 @@ * See the License for the specific language governing permissions and limitations under the License. */ -const factories = { -{{#each models}} - {{modelName}}: import('./{{modelName}}Factory'), -{{/each}} -} +import Client from './generated-client'; -export = factories; +export { + Client, +} \ No newline at end of file diff --git a/src/types/request.d.ts b/src/types/request.d.ts index 9dd1760e9..c150dea6d 100644 --- a/src/types/request.d.ts +++ b/src/types/request.d.ts @@ -1,5 +1,17 @@ +/*! + * Copyright (c) 2017-2021, Okta, Inc. and/or its affiliates. All rights reserved. + * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") + * + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and limitations under the License. + */ + declare interface Request { api(url: string): Promise; } -export default Request; \ No newline at end of file +export default Request; diff --git a/src/types/resource.d.ts b/src/types/resource.d.ts index c30d01b55..2a0ccd9a2 100644 --- a/src/types/resource.d.ts +++ b/src/types/resource.d.ts @@ -1,6 +1,6 @@ export = Resource; /*! - * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. + * Copyright (c) 2017-2021, Okta, Inc. and/or its affiliates. All rights reserved. * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") * * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. diff --git a/templates/factory.d.ts.hbs b/templates/factory.d.ts.hbs deleted file mode 100644 index 5699b7c88..000000000 --- a/templates/factory.d.ts.hbs +++ /dev/null @@ -1,21 +0,0 @@ -/*! - * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. - * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") - * - * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - * See the License for the specific language governing permissions and limitations under the License. - */ - -import ModelResolutionFactory from '../resolution-factory'; - -declare class {{parentModelName}}Factory extends ModelResolutionFactory { - getMapping(): object; - - getResolutionProperty(): string; -} - -export = {{parentModelName}}Factory; From cb3980ac767a7d3e28c761768732bbeec20e37eb Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Tue, 16 Feb 2021 17:25:48 +0200 Subject: [PATCH 14/20] unify export syntax --- src/types/generated-client.d.ts | 4 ++-- src/types/index.d.ts | 2 +- src/types/resource.d.ts | 3 ++- templates/generated-client.d.ts.hbs | 4 ++-- templates/helpers/model.js | 0 templates/index.js | 15 --------------- 6 files changed, 7 insertions(+), 21 deletions(-) delete mode 100644 templates/helpers/model.js diff --git a/src/types/generated-client.d.ts b/src/types/generated-client.d.ts index 9ce232343..144c86c33 100644 --- a/src/types/generated-client.d.ts +++ b/src/types/generated-client.d.ts @@ -1,5 +1,5 @@ /*! - * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. + * Copyright (c) 2017-2021, Okta, Inc. and/or its affiliates. All rights reserved. * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") * * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. @@ -512,4 +512,4 @@ declare class GeneratedApiClient { }): Promise; } -export = GeneratedApiClient; +export default GeneratedApiClient; diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 196e5c85f..9e9ed5c38 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -14,4 +14,4 @@ import Client from './generated-client'; export { Client, -} \ No newline at end of file +} diff --git a/src/types/resource.d.ts b/src/types/resource.d.ts index 2a0ccd9a2..2d1c5d2fc 100644 --- a/src/types/resource.d.ts +++ b/src/types/resource.d.ts @@ -1,4 +1,3 @@ -export = Resource; /*! * Copyright (c) 2017-2021, Okta, Inc. and/or its affiliates. All rights reserved. * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") @@ -14,3 +13,5 @@ export = Resource; declare class Resource { constructor(resourceJson: any, client: any); } + +export default Resource; diff --git a/templates/generated-client.d.ts.hbs b/templates/generated-client.d.ts.hbs index 27a6c8dc2..50694e2fc 100644 --- a/templates/generated-client.d.ts.hbs +++ b/templates/generated-client.d.ts.hbs @@ -1,5 +1,5 @@ /*! - * Copyright (c) 2017-2020, Okta, Inc. and/or its affiliates. All rights reserved. + * Copyright (c) 2017-2021, Okta, Inc. and/or its affiliates. All rights reserved. * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.") * * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. @@ -18,4 +18,4 @@ declare class GeneratedApiClient { {{/each}} } -export = GeneratedApiClient; +export default GeneratedApiClient; diff --git a/templates/helpers/model.js b/templates/helpers/model.js deleted file mode 100644 index e69de29bb..000000000 diff --git a/templates/index.js b/templates/index.js index 824c5c8c2..7e807384c 100644 --- a/templates/index.js +++ b/templates/index.js @@ -90,15 +90,6 @@ js.process = ({spec, operations, models, handlebars}) => { propertyName: model.resolutionStrategy.propertyName } }); - templates.push({ - src: 'factory.d.ts.hbs', - dest: `src/types/factories/${model.modelName}Factory.d.ts`, - context: { - parentModelName: model.modelName, - mapping, - propertyName: model.resolutionStrategy.propertyName - } - }); } } @@ -120,12 +111,6 @@ js.process = ({spec, operations, models, handlebars}) => { context: { models: models.filter(model => model.requiresResolution) } }); - templates.push({ - src: 'factories.index.d.ts.hbs', - dest: 'src/types/factories/index.d.ts', - context: { models: models.filter(model => model.requiresResolution) } - }); - // Add helpers // Register Operation helpers From db16983e0f85809476967104d9921be5722ca95d Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Wed, 17 Feb 2021 18:35:08 +0200 Subject: [PATCH 15/20] restrict inappropirate property overrides --- templates/helpers/operation.js | 26 +++++++++++++++++++++++--- templates/index.js | 6 ++++-- templates/model.d.ts.hbs | 2 ++ templates/model.js.hbs | 2 ++ 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index 8f313087e..2ad1c79cd 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -1,6 +1,17 @@ const _ = require('lodash'); const MODELS_SHOULD_NOT_PROCESS = ['object', 'string', 'undefined']; +const RESTRICTED_PROPERTY_OVERRIDES = { + OktaSignOnPolicy: ['conditions'], + PasswordPolicy: ['conditions'], + BookmarkApplication: ['name'], + BasicAuthApplication: ['name'], + OpenIdConnectApplication: ['name'], + WsFederationApplication: ['name'], + SwaThreeFieldApplication: ['name'], + SwaApplication: ['name'], + SecurePasswordStoreApplication: ['name'], +}; const getBodyModelName = operation => { const { bodyModel, parameters } = operation; @@ -173,8 +184,13 @@ const typeScriptModelImportBuilder = model => { ] }, []); - const propertiesImportTypes = - properties.filter(property => property.$ref).map(property => property.model); + const propertiesImportTypes = []; + properties.forEach(property => { + const shouldProcess = !MODELS_SHOULD_NOT_PROCESS.includes(property.model); + const isRestricted = isRestrictedPropertyOverride(model.modelName, property.propertyName); + if (property.$ref && shouldProcess && !isRestricted) + propertiesImportTypes.push(property.model) + }); const uniqueImportTypes = new Set([...methodsImportTypes, ...propertiesImportTypes]); uniqueImportTypes.delete(model.modelName); @@ -300,6 +316,9 @@ const sanitizeModelPropertyName = propertyName => { return sanitizedPropertyName; }; +const isRestrictedPropertyOverride = (modelName, propertyName) => { + return RESTRICTED_PROPERTY_OVERRIDES[modelName] && RESTRICTED_PROPERTY_OVERRIDES[modelName].includes(propertyName); +} module.exports = { getBodyModelNameInCamelCase, @@ -315,5 +334,6 @@ module.exports = { typeScriptModelMethodSignatureBuilder, typeScriptClientImportBuilder, convertSwaggerToTSType, - sanitizeModelPropertyName + sanitizeModelPropertyName, + isRestrictedPropertyOverride, } diff --git a/templates/index.js b/templates/index.js index 7e807384c..b5bd65fa8 100644 --- a/templates/index.js +++ b/templates/index.js @@ -137,7 +137,8 @@ js.process = ({spec, operations, models, handlebars}) => { const importStatements = new Set(); model.properties.forEach(property => { const shouldProcess = !MODELS_SHOULD_NOT_PROCESS.includes(property.model); - if (property.$ref && shouldProcess && !property.isEnum) { + const isRestricted = operationUtils.isRestrictedPropertyOverride(model.modelName, property.propertyName); + if (property.$ref && shouldProcess && !property.isEnum && !isRestricted) { importStatements.add(`const ${property.model} = require('./${property.model}');`); } }); @@ -151,7 +152,8 @@ js.process = ({spec, operations, models, handlebars}) => { const constructorStatements = []; model.properties.forEach(property => { const shouldProcess = !MODELS_SHOULD_NOT_PROCESS.includes(property.model); - if (property.$ref && shouldProcess && !property.isEnum) { + const isRestricted = operationUtils.isRestrictedPropertyOverride(model.modelName, property.propertyName); + if (property.$ref && shouldProcess && !property.isEnum && !isRestricted) { constructorStatements.push(` if (resourceJson && resourceJson.${property.propertyName}) {`); constructorStatements.push(` this.${property.propertyName} = new ${property.model}(resourceJson.${property.propertyName});`); constructorStatements.push(` }`); diff --git a/templates/model.d.ts.hbs b/templates/model.d.ts.hbs index 4c8b4258e..6d556c037 100644 --- a/templates/model.d.ts.hbs +++ b/templates/model.d.ts.hbs @@ -28,6 +28,7 @@ declare class {{modelName}} extends Resource { constructor(resourceJson: string, client: any); {{#each properties}} +{{#unless (isRestrictedPropertyOverride ../modelName this.propertyName)}} {{#if this.$ref}} {{#if (ne this.model "object") }} {{{sanitizeModelPropertyName this.propertyName}}}: {{model}}; @@ -35,6 +36,7 @@ declare class {{modelName}} extends Resource { {{else}} {{{sanitizeModelPropertyName this.propertyName}}}: {{convertSwaggerToTSType this.commonType}}; {{/if}} +{{/unless}} {{/each}} {{#each crud}} diff --git a/templates/model.js.hbs b/templates/model.js.hbs index 246d8c90e..7945fbe9a 100644 --- a/templates/model.js.hbs +++ b/templates/model.js.hbs @@ -30,7 +30,9 @@ var Resource = require('../resource'); {{#each properties}} {{#if this.$ref}} {{#if (ne this.model "object") }} +{{#unless (isRestrictedPropertyOverride ../modelName this.propertyName)}} * @property { {{model}} } {{this.propertyName}} + {{/unless}} {{/if}} {{else}} * @property { {{this.commonType}} } {{this.propertyName}} From c9d24dfbb3f8445dc278647eeb19e1dc152a0a5a Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Thu, 18 Feb 2021 12:26:58 +0200 Subject: [PATCH 16/20] reduce blocklisted models filtering duplication --- templates/helpers/operation.js | 74 +++++++++++++++++++--------------- templates/index.js | 16 ++++---- templates/model.d.ts.hbs | 4 +- 3 files changed, 51 insertions(+), 43 deletions(-) diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index 2ad1c79cd..b171eda85 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -1,7 +1,9 @@ const _ = require('lodash'); const MODELS_SHOULD_NOT_PROCESS = ['object', 'string', 'undefined']; -const RESTRICTED_PROPERTY_OVERRIDES = { + +// BEGIN work around spec mismatches and upstream parsing incosistencies +const RESTRICTED_MODEL_PROPERTY_OVERRIDES = { OktaSignOnPolicy: ['conditions'], PasswordPolicy: ['conditions'], BookmarkApplication: ['name'], @@ -12,6 +14,11 @@ const RESTRICTED_PROPERTY_OVERRIDES = { SwaApplication: ['name'], SecurePasswordStoreApplication: ['name'], }; +const KNOWN_CONFLICTING_PROPERTY_NAMES = { + UserFactor: ['verify'], +}; +const PROPERTY_NAME_CHARACTERS_REQUIRE_ESCAPING = ['#']; +// END work around spec mismatches and upstream parsing incosistencies const getBodyModelName = operation => { const { bodyModel, parameters } = operation; @@ -145,24 +152,23 @@ const jsdocBuilder = (operation) => { const typeScriptOperationSignatureBuilder = operation => { const [args, returnType] = getOperationArgumentsAndReturnType(operation); - return `${operation.operationId}(${formatArguments(args).join(', ')}): Promise<${returnType}>;`; + return `${operation.operationId}(${formatTypeScriptArguments(args)}): Promise<${returnType}>;`; }; const typeScriptModelMethodSignatureBuilder = (method, modelName) => { const [args, returnType] = getModelMethodArgumentsAndReturnType(method, modelName); - return `(${formatArguments(args).join(', ')}): Promise<${returnType}>;`; + return `(${formatTypeScriptArguments(args)}): Promise<${returnType}>;`; }; const typeScriptClientImportBuilder = operations => { const operationsImportTypes = operations.reduce((acc, operation) => { const [args, returnType] = getOperationArgumentsAndReturnType(operation); + const importableTypes = [...args.values(), returnType].filter(isImportableType); return [ ...acc, - ...args.values(), - returnType, + ...importableTypes, ] }, []); - const importStatements = formatImportStatements(new Set([...operationsImportTypes]), { isModelToModelImport: false }); @@ -177,18 +183,16 @@ const typeScriptModelImportBuilder = model => { const methodsImportTypes = model.methods.reduce((acc, method) => { const [args, returnType] = getModelMethodArgumentsAndReturnType(method, model.modelName); + const importableTypes = [...args.values(), returnType].filter(isImportableType); return [ ...acc, - ...args.values(), - returnType, + ...importableTypes, ] }, []); const propertiesImportTypes = []; properties.forEach(property => { - const shouldProcess = !MODELS_SHOULD_NOT_PROCESS.includes(property.model); - const isRestricted = isRestrictedPropertyOverride(model.modelName, property.propertyName); - if (property.$ref && shouldProcess && !isRestricted) + if (isImportablePropertyType(property, model.modelName)) propertiesImportTypes.push(property.model) }); @@ -249,7 +253,7 @@ const getModelMethodArgumentsAndReturnType = (method, modelName) => { return [args, returnType]; }; -const formatArguments = args => { +const formatTypeScriptArguments = args => { const typedArgs = []; for (let [arg, argType] of args) { if (Array.isArray(argType)) { @@ -258,7 +262,7 @@ const formatArguments = args => { typedArgs.push(`${arg}: ${argType}`) } } - return typedArgs; + return typedArgs.join(', '); } const formatObjectLiteralType = typeProps => { @@ -270,18 +274,15 @@ const formatObjectLiteralType = typeProps => { return objectLiteralType; } -const formatImportStatements = (importTypes, formattingOptions = { - isModelToModelImport: true -}) => { +const formatImportStatements = (importTypes, { + isModelToModelImport = true +} = {}) => { const importStatements = []; - const { isModelToModelImport } = formattingOptions; importTypes.forEach(type => { - if (!MODELS_SHOULD_NOT_PROCESS.includes(type) && !Array.isArray(type)) { - if (type === 'Collection') { - importStatements.push(`import Collection from '${isModelToModelImport ? '..' : '.'}/collection';`); - } else { - importStatements.push(`import ${type} from '${isModelToModelImport ? './' : './models/'}${type}';`); - } + if (type === 'Collection') { + importStatements.push(`import Collection from '${isModelToModelImport ? '..' : '.'}/collection';`); + } else { + importStatements.push(`import ${type} from '${isModelToModelImport ? './' : './models/'}${type}';`); } }); return importStatements.join('\n'); @@ -298,14 +299,26 @@ const convertSwaggerToTSType = swaggerType => { }[swaggerType] || swaggerType; }; -const sanitizeModelPropertyName = propertyName => { - const restrictedChars = ['#']; - const knownConflictingPropertyNames = ['verify']; +const isImportablePropertyType = (property, hostModelName) => { + const isRestricted = isRestrictedPropertyOverride(hostModelName, property.propertyName); + return property.$ref && isImportableType(property.model) && !isRestricted; +} + +const isRestrictedPropertyOverride = (modelName, propertyName) => { + return RESTRICTED_MODEL_PROPERTY_OVERRIDES[modelName] && + RESTRICTED_MODEL_PROPERTY_OVERRIDES[modelName].includes(propertyName); +} +const isImportableType = type => + !MODELS_SHOULD_NOT_PROCESS.includes(type) && !Array.isArray(type) + +const sanitizeModelPropertyName = (modelName, propertyName) => { let sanitizedPropertyName = propertyName; - const containsRestrictedChars = restrictedChars.find(char => propertyName.includes(char)); + const containsRestrictedChars = + PROPERTY_NAME_CHARACTERS_REQUIRE_ESCAPING.find(char => propertyName.includes(char)); - if (knownConflictingPropertyNames.includes(propertyName)) { + if (KNOWN_CONFLICTING_PROPERTY_NAMES[modelName] && + KNOWN_CONFLICTING_PROPERTY_NAMES[modelName].includes(propertyName)) { sanitizedPropertyName = `_${propertyName}`; } @@ -316,10 +329,6 @@ const sanitizeModelPropertyName = propertyName => { return sanitizedPropertyName; }; -const isRestrictedPropertyOverride = (modelName, propertyName) => { - return RESTRICTED_PROPERTY_OVERRIDES[modelName] && RESTRICTED_PROPERTY_OVERRIDES[modelName].includes(propertyName); -} - module.exports = { getBodyModelNameInCamelCase, operationArgumentBuilder, @@ -335,5 +344,6 @@ module.exports = { typeScriptClientImportBuilder, convertSwaggerToTSType, sanitizeModelPropertyName, + isImportablePropertyType, isRestrictedPropertyOverride, } diff --git a/templates/index.js b/templates/index.js index b5bd65fa8..c36258b4c 100644 --- a/templates/index.js +++ b/templates/index.js @@ -30,8 +30,6 @@ js.process = ({spec, operations, models, handlebars}) => { // Collect all the operations - const MODELS_SHOULD_NOT_PROCESS = ['object', 'string']; - const templates = []; const extensibleModels = new Set(); @@ -136,9 +134,8 @@ js.process = ({spec, operations, models, handlebars}) => { } const importStatements = new Set(); model.properties.forEach(property => { - const shouldProcess = !MODELS_SHOULD_NOT_PROCESS.includes(property.model); - const isRestricted = operationUtils.isRestrictedPropertyOverride(model.modelName, property.propertyName); - if (property.$ref && shouldProcess && !property.isEnum && !isRestricted) { + const shouldProcess = operationUtils.isImportablePropertyType(property, model.modelName); + if (shouldProcess && !property.isEnum) { importStatements.add(`const ${property.model} = require('./${property.model}');`); } }); @@ -151,9 +148,8 @@ js.process = ({spec, operations, models, handlebars}) => { } const constructorStatements = []; model.properties.forEach(property => { - const shouldProcess = !MODELS_SHOULD_NOT_PROCESS.includes(property.model); - const isRestricted = operationUtils.isRestrictedPropertyOverride(model.modelName, property.propertyName); - if (property.$ref && shouldProcess && !property.isEnum && !isRestricted) { + const shouldProcess = operationUtils.isImportablePropertyType(property, model.modelName); + if (shouldProcess && !property.isEnum) { constructorStatements.push(` if (resourceJson && resourceJson.${property.propertyName}) {`); constructorStatements.push(` this.${property.propertyName} = new ${property.model}(resourceJson.${property.propertyName});`); constructorStatements.push(` }`); @@ -190,7 +186,9 @@ js.process = ({spec, operations, models, handlebars}) => { const args = []; const operation = method.operation; - + if (modelName === 'UserFactor') { + console.log(method) + } operation.pathParams.forEach(param => { const matchingArgument = method.arguments.filter(argument => argument.dest === param.name)[0]; if (matchingArgument && matchingArgument.src){ diff --git a/templates/model.d.ts.hbs b/templates/model.d.ts.hbs index 6d556c037..7c29e3479 100644 --- a/templates/model.d.ts.hbs +++ b/templates/model.d.ts.hbs @@ -31,10 +31,10 @@ declare class {{modelName}} extends Resource { {{#unless (isRestrictedPropertyOverride ../modelName this.propertyName)}} {{#if this.$ref}} {{#if (ne this.model "object") }} - {{{sanitizeModelPropertyName this.propertyName}}}: {{model}}; + {{{sanitizeModelPropertyName ../modelName this.propertyName}}}: {{model}}; {{/if}} {{else}} - {{{sanitizeModelPropertyName this.propertyName}}}: {{convertSwaggerToTSType this.commonType}}; + {{{sanitizeModelPropertyName ../modelName this.propertyName}}}: {{convertSwaggerToTSType this.commonType}}; {{/if}} {{/unless}} {{/each}} From 861f74ab50c73be0e7246b3968487b0714250511 Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Thu, 18 Feb 2021 12:53:57 +0200 Subject: [PATCH 17/20] add comment on model return type --- templates/helpers/operation.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index b171eda85..d5f793fc0 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -169,10 +169,10 @@ const typeScriptClientImportBuilder = operations => { ...importableTypes, ] }, []); - const importStatements = formatImportStatements(new Set([...operationsImportTypes]), { + + return formatImportStatements(new Set([...operationsImportTypes]), { isModelToModelImport: false }); - return importStatements; }; const typeScriptModelImportBuilder = model => { @@ -196,9 +196,10 @@ const typeScriptModelImportBuilder = model => { propertiesImportTypes.push(property.model) }); - const uniqueImportTypes = new Set([...methodsImportTypes, ...propertiesImportTypes]); - uniqueImportTypes.delete(model.modelName); - return formatImportStatements(uniqueImportTypes); + const importTypes = new Set([...methodsImportTypes, ...propertiesImportTypes]); + // model methods returning model type + importTypes.delete(model.modelName); + return formatImportStatements(importTypes); }; const getOperationArgumentsAndReturnType = operation => { From 4403d0ac20721123524b68e67b17e49b53e8fffa Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Thu, 18 Feb 2021 12:58:24 +0200 Subject: [PATCH 18/20] export model types --- src/types/index.d.ts | 2 ++ templates/index.js | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 9e9ed5c38..aeb193067 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -11,7 +11,9 @@ */ import Client from './generated-client'; +import * as Models from './models'; export { Client, + Models, } diff --git a/templates/index.js b/templates/index.js index c36258b4c..3b0644e3a 100644 --- a/templates/index.js +++ b/templates/index.js @@ -186,9 +186,7 @@ js.process = ({spec, operations, models, handlebars}) => { const args = []; const operation = method.operation; - if (modelName === 'UserFactor') { - console.log(method) - } + operation.pathParams.forEach(param => { const matchingArgument = method.arguments.filter(argument => argument.dest === param.name)[0]; if (matchingArgument && matchingArgument.src){ From d1d7d92c1e43b8f49af0c14d62a62e7ed1669dff Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Fri, 19 Feb 2021 13:44:07 +0200 Subject: [PATCH 19/20] enable eslint on templates dir --- .eslintignore | 1 + templates/.eslintrc | 16 +++++++++ templates/helpers/operation.js | 61 +++++++++++++++++----------------- templates/index.js | 20 +++++------ 4 files changed, 56 insertions(+), 42 deletions(-) create mode 100644 .eslintignore create mode 100644 templates/.eslintrc diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..73e6e7bb8 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +test/jest/coverage/ diff --git a/templates/.eslintrc b/templates/.eslintrc new file mode 100644 index 000000000..b93d815a5 --- /dev/null +++ b/templates/.eslintrc @@ -0,0 +1,16 @@ +{ + "parserOptions": { + "ecmaVersion": 8 + }, + "rules": { + "no-use-before-define": ["error", {"functions": false, "variables": false}] + }, + "env": { + "node": true, + "es6": true + }, + "extends": [ + "../src/.eslintrc", + "eslint:recommended" + ] +} diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index d5f793fc0..61f5b7efa 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -54,19 +54,19 @@ const getOperationArgument = operation => { } return args; -} +}; const operationArgumentBuilder = (operation) => { const args = getOperationArgument(operation); return args.join(', '); -} +}; -const getRequiredOperationParams = operation => { +const getRequiredOperationParams = operation => { const args = getOperationArgument(operation); return operation.parameters.filter(({ name, required }) => { return args.includes(name) && required; }); -} +}; const getHttpMethod = ({ consumes, produces, method, responseModel }) => { let res; @@ -99,15 +99,15 @@ const hasRequest = (operation) => { }; const hasHeaders = (operation) => { - const { consumes, produces, bodyModel } = operation; + const { consumes, produces } = operation; const httpMethod = getHttpMethod(operation); return httpMethod.indexOf('Json') === -1 && (consumes.length || produces.length); }; const shouldResolveJson = (operation) => { - return hasHeaders(operation) - && hasRequest(operation) - && operation.responseModel + return hasHeaders(operation) + && hasRequest(operation) + && operation.responseModel && operation.produces.includes('application/json'); }; @@ -141,9 +141,9 @@ const jsdocBuilder = (operation) => { if (operation.responseModel) { if (operation.isArray) { - lines.push(` * @returns {Promise} A collection that will yield {@link ${operation.responseModel}} instances.`) + lines.push(` * @returns {Promise} A collection that will yield {@link ${operation.responseModel}} instances.`); } else { - lines.push(` * @returns {Promise<${operation.responseModel}>}`) + lines.push(` * @returns {Promise<${operation.responseModel}>}`); } } @@ -167,7 +167,7 @@ const typeScriptClientImportBuilder = operations => { return [ ...acc, ...importableTypes, - ] + ]; }, []); return formatImportStatements(new Set([...operationsImportTypes]), { @@ -187,13 +187,14 @@ const typeScriptModelImportBuilder = model => { return [ ...acc, ...importableTypes, - ] + ]; }, []); const propertiesImportTypes = []; properties.forEach(property => { - if (isImportablePropertyType(property, model.modelName)) - propertiesImportTypes.push(property.model) + if (isImportablePropertyType(property, model.modelName)) { + propertiesImportTypes.push(property.model); + } }); const importTypes = new Set([...methodsImportTypes, ...propertiesImportTypes]); @@ -207,7 +208,7 @@ const getOperationArgumentsAndReturnType = operation => { const args = new Map(); pathParams.forEach(pathParam => { - args.set(pathParam.name, 'string') + args.set(pathParam.name, 'string'); }); if ((method === 'post' || method === 'put') && bodyModel) { @@ -237,12 +238,12 @@ const getOperationArgumentsAndReturnType = operation => { }; const getModelMethodArgumentsAndReturnType = (method, modelName) => { - const { operation, arguments } = method; + const { operation } = method; const [args, returnType] = getOperationArgumentsAndReturnType(operation); operation.pathParams.forEach(param => { - const matchingArgument = arguments.find(argument => argument.dest === param.name); - if (matchingArgument){ + const matchingArgument = method.arguments.find(argument => argument.dest === param.name); + if (matchingArgument) { args.delete(param.name); } }); @@ -258,22 +259,22 @@ const formatTypeScriptArguments = args => { const typedArgs = []; for (let [arg, argType] of args) { if (Array.isArray(argType)) { - typedArgs.push(`${arg}: ${formatObjectLiteralType(argType)}`) + typedArgs.push(`${arg}: ${formatObjectLiteralType(argType)}`); } else { - typedArgs.push(`${arg}: ${argType}`) + typedArgs.push(`${arg}: ${argType}`); } } return typedArgs.join(', '); -} +}; const formatObjectLiteralType = typeProps => { let objectLiteralType = '{ \n'; typeProps.forEach(prop => { - objectLiteralType += ` ${prop}: string,\n` - }) + objectLiteralType += ` ${prop}: string,\n`; + }); objectLiteralType += ' }'; return objectLiteralType; -} +}; const formatImportStatements = (importTypes, { isModelToModelImport = true @@ -287,14 +288,14 @@ const formatImportStatements = (importTypes, { } }); return importStatements.join('\n'); -} +}; const convertSwaggerToTSType = swaggerType => { return { array: '[]', integer: 'number', double: 'number', - hash: '{\n\ [name: string]: unknown;\n\ }', + hash: '{\n [name: string]: unknown;\n }', dateTime: 'string', password: 'string', }[swaggerType] || swaggerType; @@ -303,14 +304,14 @@ const convertSwaggerToTSType = swaggerType => { const isImportablePropertyType = (property, hostModelName) => { const isRestricted = isRestrictedPropertyOverride(hostModelName, property.propertyName); return property.$ref && isImportableType(property.model) && !isRestricted; -} +}; const isRestrictedPropertyOverride = (modelName, propertyName) => { return RESTRICTED_MODEL_PROPERTY_OVERRIDES[modelName] && RESTRICTED_MODEL_PROPERTY_OVERRIDES[modelName].includes(propertyName); -} +}; const isImportableType = type => - !MODELS_SHOULD_NOT_PROCESS.includes(type) && !Array.isArray(type) + !MODELS_SHOULD_NOT_PROCESS.includes(type) && !Array.isArray(type); const sanitizeModelPropertyName = (modelName, propertyName) => { let sanitizedPropertyName = propertyName; @@ -347,4 +348,4 @@ module.exports = { sanitizeModelPropertyName, isImportablePropertyType, isRestrictedPropertyOverride, -} +}; diff --git a/templates/index.js b/templates/index.js index 3b0644e3a..309b3fe42 100644 --- a/templates/index.js +++ b/templates/index.js @@ -9,7 +9,7 @@ const operationUtils = require('./helpers/operation'); */ class ModelResolver { constructor(models) { - this.models = models + this.models = models; } getByName(name) { const match = this.models.filter(model => model.modelName === name); @@ -32,10 +32,6 @@ js.process = ({spec, operations, models, handlebars}) => { const templates = []; - const extensibleModels = new Set(); - - const modelGraph = {} ; - const modelResolver = new ModelResolver(models); // Add a property to the operation that lets the client template know if the response needs resolution @@ -152,7 +148,7 @@ js.process = ({spec, operations, models, handlebars}) => { if (shouldProcess && !property.isEnum) { constructorStatements.push(` if (resourceJson && resourceJson.${property.propertyName}) {`); constructorStatements.push(` this.${property.propertyName} = new ${property.model}(resourceJson.${property.propertyName});`); - constructorStatements.push(` }`); + constructorStatements.push(' }'); } }); return constructorStatements.join('\n'); @@ -165,7 +161,7 @@ js.process = ({spec, operations, models, handlebars}) => { operation.pathParams.forEach(param => { const matchingArgument = method.arguments.filter(argument => argument.dest === param.name)[0]; - if (!matchingArgument || !matchingArgument.src){ + if (!matchingArgument || !matchingArgument.src) { args.push(param.name); } }); @@ -189,7 +185,7 @@ js.process = ({spec, operations, models, handlebars}) => { operation.pathParams.forEach(param => { const matchingArgument = method.arguments.filter(argument => argument.dest === param.name)[0]; - if (matchingArgument && matchingArgument.src){ + if (matchingArgument && matchingArgument.src) { args.push(`this.${matchingArgument.src}`); } else { args.push(param.name); @@ -215,7 +211,7 @@ js.process = ({spec, operations, models, handlebars}) => { operation.pathParams.forEach(param => { const matchingArgument = method.arguments.filter(argument => argument.dest === param.name)[0]; - if (!matchingArgument || !matchingArgument.src){ + if (!matchingArgument || !matchingArgument.src) { args.push(`@param {${param.type}} ${param.name}`); } }); @@ -230,9 +226,9 @@ js.process = ({spec, operations, models, handlebars}) => { if (operation.responseModel) { if (operation.isArray) { - args.push(`@returns {Promise} A collection that will yield {@link ${operation.responseModel}} instances.`) + args.push(`@returns {Promise} A collection that will yield {@link ${operation.responseModel}} instances.`); } else { - args.push(`@returns {Promise<${operation.responseModel}>}`) + args.push(`@returns {Promise<${operation.responseModel}>}`); } } @@ -247,7 +243,7 @@ js.process = ({spec, operations, models, handlebars}) => { handlebars.registerHelper('getAffectedResources', (path) => { const resources = []; let pl = path.length; - while(pl--) { + while (pl--) { if (path[pl] === '}') { const resourcePath = path.slice(0, pl + 1).replace(/{/g, '${'); resources.push('${this.baseUrl}' + resourcePath); From 3a9bc476c0e2893874042c3ad00d178e2016f1de Mon Sep 17 00:00:00 2001 From: Oleksandr Pravosudko Date: Fri, 19 Feb 2021 14:08:00 +0200 Subject: [PATCH 20/20] CR: add a note on blocklisted properties --- templates/helpers/operation.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/templates/helpers/operation.js b/templates/helpers/operation.js index 61f5b7efa..a72b5571b 100644 --- a/templates/helpers/operation.js +++ b/templates/helpers/operation.js @@ -2,7 +2,12 @@ const _ = require('lodash'); const MODELS_SHOULD_NOT_PROCESS = ['object', 'string', 'undefined']; -// BEGIN work around spec mismatches and upstream parsing incosistencies +// BEGIN Work around spec mismatches and upstream parsing inconsistencies. +// The below map is used to block redefining(overriding) properties listed as values in models specified as keys. +// It is also consulted with for excluding the blocklisted properties from intialization in constructor, +// adding to jsDoc parameters and skipping imports. +// This is done to avoid property type conflicts between parent and descendant models. +// These models' respective superclasses are expected to provide a correct property type. const RESTRICTED_MODEL_PROPERTY_OVERRIDES = { OktaSignOnPolicy: ['conditions'], PasswordPolicy: ['conditions'], @@ -18,7 +23,7 @@ const KNOWN_CONFLICTING_PROPERTY_NAMES = { UserFactor: ['verify'], }; const PROPERTY_NAME_CHARACTERS_REQUIRE_ESCAPING = ['#']; -// END work around spec mismatches and upstream parsing incosistencies +// END Work around spec mismatches and upstream parsing inconsistencies. const getBodyModelName = operation => { const { bodyModel, parameters } = operation;