Zapier generator (#15997)

* First version of Zapier Generator

* fixed zapier codegen

* added zapier templates

* added zapier sample

* added zapier doc

* added zapier generator form data management

* added samples generation

* updated docs

* fixed zapier api template

* fixed zapier samples export

* added zapier readme template

* fixed zapier readme template

* added petstore readme

* cleaned zapier generator

* updated samples

* fixed zapier enum label

* cleaned code

* updated samples

* improved zapier search actions

* updated samples

---------

Co-authored-by: Mauro Valota <maurovalota@fattureincloud.it>
Co-authored-by: William Cheng <wing328hk@gmail.com>
This commit is contained in:
Emanuele Saccomandi 2023-07-09 15:52:04 +02:00 committed by GitHub
parent f63ef71432
commit 9baf4988f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 2123 additions and 1 deletions

View File

@ -0,0 +1,7 @@
generatorName: zapier
outputDir: samples/client/petstore/zapier
inputSpec: modules/openapi-generator/src/test/resources/3_0/petstore.yaml
templateDir: modules/openapi-generator/src/main/resources/zapier
additionalProperties:
npmVersion: 0.0.1
npmName: '@openapitools/zapier'

View File

@ -74,6 +74,7 @@ The following generators are available:
* [typescript-redux-query](generators/typescript-redux-query.md) * [typescript-redux-query](generators/typescript-redux-query.md)
* [typescript-rxjs](generators/typescript-rxjs.md) * [typescript-rxjs](generators/typescript-rxjs.md)
* [xojo-client](generators/xojo-client.md) * [xojo-client](generators/xojo-client.md)
* [zapier](generators/zapier.md)
## SERVER generators ## SERVER generators

178
docs/generators/zapier.md Normal file
View File

@ -0,0 +1,178 @@
---
title: Documentation for the zapier Generator
---
## METADATA
| Property | Value | Notes |
| -------- | ----- | ----- |
| generator name | zapier | pass this to the generate command after -g |
| generator stability | STABLE | |
| generator type | CLIENT | |
| generator default templating engine | mustache | |
| helpTxt | Generates a zapier client. | |
## CONFIG OPTIONS
These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details.
| Option | Description | Values | Default |
| ------ | ----------- | ------ | ------- |
|allowUnicodeIdentifiers|boolean, toggles whether unicode identifiers are allowed in names or not, default is false| |false|
|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.</dd></dl>|true|
|ensureUniqueParams|Whether to ensure parameter names are unique in an operation (rename parameters that are not).| |true|
|enumUnknownDefaultCase|If the server adds new enum cases, that are unknown by an old spec/client, the client will fail to parse the network response.With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the server sends an enum case that is not known by the client/spec, they can safely fallback to this case.|<dl><dt>**false**</dt><dd>No changes to the enum's are made, this is the default option.</dd><dt>**true**</dt><dd>With this option enabled, each enum will have a new case, 'unknown_default_open_api', so that when the enum case sent by the server is not known by the client/spec, can safely be decoded to this case.</dd></dl>|false|
|legacyDiscriminatorBehavior|Set to false for generators with better support for discriminators. (Python, Java, Go, PowerShell, C# have this enabled by default).|<dl><dt>**true**</dt><dd>The mapping in the discriminator includes descendent schemas that allOf inherit from self and the discriminator mapping schemas in the OAS document.</dd><dt>**false**</dt><dd>The mapping in the discriminator includes any descendent schemas that allOf inherit from self, any oneOf schemas, any anyOf schemas, any x-discriminator-values, and the discriminator mapping schemas in the OAS document AND Codegen validates that oneOf and anyOf schemas contain the required discriminator and throws an error if the discriminator is missing.</dd></dl>|true|
|prependFormOrBodyParameters|Add form or body parameters to the beginning of the parameter list.| |false|
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
## IMPORT MAPPING
| Type/Alias | Imports |
| ---------- | ------- |
## INSTANTIATION TYPES
| Type/Alias | Instantiated By |
| ---------- | --------------- |
|array|array|
|list|array|
|map|object|
|set|array|
## LANGUAGE PRIMITIVES
<ul class="column-ul">
<li>array</li>
<li>boolean</li>
<li>file</li>
<li>integer</li>
<li>number</li>
<li>object</li>
<li>string</li>
</ul>
## RESERVED WORDS
<ul class="column-ul">
</ul>
## FEATURE SET
### Client Modification Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|BasePath|✗|ToolingExtension
|Authorizations|✗|ToolingExtension
|UserAgent|✗|ToolingExtension
|MockServer|✗|ToolingExtension
### Data Type Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Custom|✗|OAS2,OAS3
|Int32|✓|OAS2,OAS3
|Int64|✓|OAS2,OAS3
|Float|✓|OAS2,OAS3
|Double|✓|OAS2,OAS3
|Decimal|✓|ToolingExtension
|String|✓|OAS2,OAS3
|Byte|✓|OAS2,OAS3
|Binary|✓|OAS2,OAS3
|Boolean|✓|OAS2,OAS3
|Date|✓|OAS2,OAS3
|DateTime|✓|OAS2,OAS3
|Password|✓|OAS2,OAS3
|File|✓|OAS2
|Uuid|✗|
|Array|✓|OAS2,OAS3
|Null|✗|OAS3
|AnyType|✗|OAS2,OAS3
|Object|✓|OAS2,OAS3
|Maps|✓|ToolingExtension
|CollectionFormat|✓|OAS2
|CollectionFormatMulti|✓|OAS2
|Enum|✓|OAS2,OAS3
|ArrayOfEnum|✓|ToolingExtension
|ArrayOfModel|✓|ToolingExtension
|ArrayOfCollectionOfPrimitives|✓|ToolingExtension
|ArrayOfCollectionOfModel|✓|ToolingExtension
|ArrayOfCollectionOfEnum|✓|ToolingExtension
|MapOfEnum|✓|ToolingExtension
|MapOfModel|✓|ToolingExtension
|MapOfCollectionOfPrimitives|✓|ToolingExtension
|MapOfCollectionOfModel|✓|ToolingExtension
|MapOfCollectionOfEnum|✓|ToolingExtension
### Documentation Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Readme|✗|ToolingExtension
|Model|✓|ToolingExtension
|Api|✓|ToolingExtension
### Global Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Host|✓|OAS2,OAS3
|BasePath|✓|OAS2,OAS3
|Info|✓|OAS2,OAS3
|Schemes|✗|OAS2,OAS3
|PartialSchemes|✓|OAS2,OAS3
|Consumes|✓|OAS2
|Produces|✓|OAS2
|ExternalDocumentation|✓|OAS2,OAS3
|Examples|✓|OAS2,OAS3
|XMLStructureDefinitions|✗|OAS2,OAS3
|MultiServer|✗|OAS3
|ParameterizedServer|✗|OAS3
|ParameterStyling|✗|OAS3
|Callbacks|✓|OAS3
|LinkObjects|✗|OAS3
### Parameter Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Path|✓|OAS2,OAS3
|Query|✓|OAS2,OAS3
|Header|✓|OAS2,OAS3
|Body|✓|OAS2
|FormUnencoded|✓|OAS2
|FormMultipart|✓|OAS2
|Cookie|✓|OAS3
### Schema Support Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|Simple|✓|OAS2,OAS3
|Composite|✓|OAS2,OAS3
|Polymorphism|✓|OAS2,OAS3
|Union|✗|OAS3
|allOf|✗|OAS2,OAS3
|anyOf|✗|OAS3
|oneOf|✗|OAS3
|not|✗|OAS3
### Security Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|BasicAuth|✓|OAS2,OAS3
|ApiKey|✓|OAS2,OAS3
|OpenIDConnect|✗|OAS3
|BearerToken|✓|OAS3
|OAuth2_Implicit|✓|OAS2,OAS3
|OAuth2_Password|✓|OAS2,OAS3
|OAuth2_ClientCredentials|✓|OAS2,OAS3
|OAuth2_AuthorizationCode|✓|OAS2,OAS3
|SignatureAuth|✗|OAS3
### Wire Format Feature
| Name | Supported | Defined By |
| ---- | --------- | ---------- |
|JSON|✓|OAS2,OAS3
|XML|✓|OAS2,OAS3
|PROTOBUF|✗|ToolingExtension
|Custom|✗|OAS2,OAS3

View File

@ -0,0 +1,199 @@
package org.openapitools.codegen.languages;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.responses.ApiResponse;
import org.openapitools.codegen.*;
import org.openapitools.codegen.utils.ModelUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.*;
import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER;
import static org.openapitools.codegen.utils.StringUtils.camelize;
import static org.openapitools.codegen.utils.StringUtils.escape;
public class ZapierClientCodegen extends DefaultCodegen implements CodegenConfig {
public static final String PROJECT_NAME = "projectName";
private final Logger LOGGER = LoggerFactory.getLogger(ZapierClientCodegen.class);
public CodegenType getTag() {
return CodegenType.CLIENT;
}
public String getName() {
return "zapier";
}
public String getHelp() {
return "Generates a zapier client.";
}
public ZapierClientCodegen() {
super();
outputFolder = "generated-code" + File.separator + "zapier";
modelTemplateFiles.put("model.mustache", ".js");
apiTemplateFiles.put("api.mustache", ".js");
embeddedTemplateDir = templateDir = "zapier";
apiPackage = "apis";
testPackage = "samples";
modelPackage = "models";
apiTestTemplateFiles.put("sample.mustache", ".js");
supportingFiles.add(new SupportingFile("actions.mustache", "operations", "actions.js"));
supportingFiles.add(new SupportingFile("utils.mustache", "utils", "utils.js"));
supportingFiles.add(new SupportingFile("index.mustache", "", "index.js"));
supportingFiles.add(new SupportingFile("authentication.mustache", "", "authentication.js"));
supportingFiles.add(new SupportingFile("package.mustache", "", "package.json"));
supportingFiles.add(new SupportingFile("README.mustache", "", "README.md"));
languageSpecificPrimitives.clear();
languageSpecificPrimitives = new HashSet<>(
Arrays.asList("number", "integer", "string", "boolean", "array", "file", "object")
);
instantiationTypes.put("array", "array");
instantiationTypes.put("set", "array");
instantiationTypes.put("list", "array");
instantiationTypes.put("map", "object");
typeMapping = new HashMap<>();
typeMapping.put("array", "array");
typeMapping.put("set", "array");
typeMapping.put("map", "object");
typeMapping.put("List", "array");
typeMapping.put("boolean", "boolean");
typeMapping.put("string", "string");
typeMapping.put("int", "integer");
typeMapping.put("float", "number");
typeMapping.put("number", "number");
typeMapping.put("decimal", "number");
typeMapping.put("DateTime", "string");
typeMapping.put("date", "string");
typeMapping.put("long", "number");
typeMapping.put("short", "number");
typeMapping.put("char", "string");
typeMapping.put("double", "number");
typeMapping.put("object", "object");
typeMapping.put("integer", "integer");
typeMapping.put("binary", "file");
typeMapping.put("file", "file");
typeMapping.put("UUID", "string");
typeMapping.put("URI", "string");
}
@Override
protected void initializeSpecialCharacterMapping() {
super.initializeSpecialCharacterMapping();
specialCharReplacements.remove("_");
}
/**
* Works identically to {@link DefaultCodegen#toParamName(String)} but doesn't camelize.
*
* @param name Codegen property object
* @return the sanitized parameter name
*/
@Override
public String toParamName(String name) {
if (reservedWords.contains(name)) {
return escapeReservedWord(name);
} else if (((CharSequence) name).chars().anyMatch(character -> specialCharReplacements.keySet().contains(String.valueOf((char) character)))) {
return escape(name, specialCharReplacements, null, null);
}
return name;
}
@Override
public String toModelName(final String name) {
return name;
}
@Override
public String toModelImport(String name) {
return "const " + name + " = " + "require('../" + modelPackage() + "/" + name + "');";
}
@Override
public String getSchemaType(Schema p) {
String openAPIType = super.getSchemaType(p);
String type = null;
if (typeMapping.containsKey(openAPIType)) {
type = typeMapping.get(openAPIType);
if (!needToImport(type)) {
return type;
}
} else {
type = openAPIType;
}
if (null == type) {
LOGGER.error("No Type defined for Schema {}", p);
}
return toModelName(type);
}
@Override
public String toModelFilename(String name) {
return name;
}
@Override
public String toApiTestFilename(String name) {
return toApiName(name);
}
@Override
public GeneratorLanguage generatorLanguage() { return null; }
@Override
public String escapeUnsafeCharacters(String input) {
return input;
}
@Override
public String escapeQuotationMark(String input) {
return input;
}
@Override
public CodegenResponse fromResponse(String responseCode, ApiResponse response) {
CodegenResponse r = super.fromResponse(responseCode, response);
try {
Map<String, Map<String, Map<String, Object>>> map = Json.mapper().readerFor(Map.class).readValue(Json.pretty(response.getContent()));
Map.Entry<String, Map<String, Map<String, Object>>> entry = map.entrySet().stream().findFirst().get();
Map<String, Map<String, Object>> example = entry.getValue();
r.examples = toExamples(example.get("examples"));
} catch (Exception e) {
LOGGER.error(e.toString());
}
return r;
}
@Override
protected List<Map<String, Object>> toExamples(Map<String, Object> examples) {
if (examples == null) {
return null;
}
final List<Map<String, Object>> output = new ArrayList<>(examples.size());
for (Map.Entry<String, Object> entry : examples.entrySet()) {
final Map<String, Object> kv = new HashMap<>();
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) entry.getValue();
String example = "";
try{
example = Json.mapper().writeValueAsString(map.getOrDefault("value", map));
} catch(Exception ignored) {}
kv.put("example", example);
output.add(kv);
}
return output;
}
}

View File

@ -140,3 +140,4 @@ org.openapitools.codegen.languages.TypeScriptReduxQueryClientCodegen
org.openapitools.codegen.languages.TypeScriptRxjsClientCodegen org.openapitools.codegen.languages.TypeScriptRxjsClientCodegen
org.openapitools.codegen.languages.WsdlSchemaCodegen org.openapitools.codegen.languages.WsdlSchemaCodegen
org.openapitools.codegen.languages.XojoClientCodegen org.openapitools.codegen.languages.XojoClientCodegen
org.openapitools.codegen.languages.ZapierClientCodegen

View File

@ -0,0 +1,51 @@
# Documentation for zapier generator
This generator generates a partial implementation of a zapier integration from an openapi specification.
## Why is it partial?
It's a partial integration because you have to put in some code for it to be 100% complete, code that depends only on how your api is structured.
Here there are all the parts you need to complete by yourself:
### 1) **utils/utils.js** isSearchAction method
This method has to return either true or false if a method is a [zapier search action](https://platform.zapier.com/docs/search-create).
```
const isSearchAction = (key) => {
// TODO: custom logic
return false
}
```
### 2) **utils/utils.js** searchMiddleware method
This method has to return an array of resources (searches must return an array), if you have pagination or you api returns an array inside nested fields, here you have to extract the array.
```
const searchMiddleware = (action) => {
// TODO: custom logic
return action
}
```
### 3) **authentication.js**
This file must be written completely according to your api authentication, you can get it generated automatically by creating the integration on the zapier website and filling the requested data, or you can build it yourself following [this guide](https://platform.zapier.com/cli_tutorials/getting-started#adding-authentication).
## Samples
To get your app made public on zapier you must provide a sample (json example response) for each of your actions, these samples get automatically generated by this generator but you have to have actual response examples in you openapi file.
For example:
```
CreateUserResponse:
description: Example response
content:
application/json:
schema:
$ref: ./models/responses/CreateUserResponse.yaml
examples:
example-1:
value:
data:
id: 12345
name: user1
```

View File

@ -0,0 +1,23 @@
{{#apiInfo}}
{{#apis}}
const {{classname}} = require('../{{apiPackage}}/{{classname}}');
{{/apis}}
{{/apiInfo}}
const { searchMiddleware, hasSearchRequisites, isSearchAction } = require('../utils/utils');
const actions = {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
[{{classname}}.{{operationId}}.key]: {{classname}}.{{operationId}},
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
}
module.exports = {
searchActions: () => Object.entries(actions).reduce((actions, [key, value]) => isSearchAction(key) && hasSearchRequisites(value) ? {...actions, [key]: searchMiddleware(value)} : actions, {}),
createActions: () => Object.entries(actions).reduce((actions, [key, value]) => !isSearchAction(key) ? {...actions, [key]: value} : actions, {}),
}

View File

@ -0,0 +1,106 @@
const samples = require('../samples/{{classFilename}}');
{{#imports}}{{{import}}}
{{/imports}}
const utils = require('../utils/utils');
{{#operations}}
{{#operation}}
{{#isMultipart}}
const FormData = require('form-data');
{{/isMultipart}}
{{/operation}}
{{/operations}}
{{#operations}}
module.exports = {
{{#operation}}
{{operationId}}: {
key: '{{operationId}}',
noun: '{{baseName}}',
display: {
label: '{{operationId}}',
description: '{{#notes}}{{.}}{{/notes}}',
hidden: false,
},
operation: {
inputFields: [
{{#allParams}}
{{#isPrimitiveType}}
{
key: '{{baseName}}',
label: '{{description}}',
type: '{{#isInteger}}integer{{/isInteger}}{{^isInteger}}{{#isNumeric}}number{{/isNumeric}}{{/isInteger}}{{#isString}}string{{/isString}}{{#isBoolean}}boolean{{/isBoolean}}{{#isDateTime}}datetime{{/isDateTime}}{{#isDate}}datetime{{/isDate}}{{#isFile}}file{{/isFile}}',
{{#required}}
required: true,
{{/required}}
{{#isEnum}}
choices: [
{{#_enum}}
'{{.}}',
{{/_enum}}
],
{{/isEnum}}
},
{{/isPrimitiveType}}
{{^isPrimitiveType}}
...{{baseType}}.fields(),
{{/isPrimitiveType}}
{{/allParams}}
],
outputFields: [
{{#returnType}}
{{^returnTypeIsPrimitive}}
...{{returnType}}.fields('', false),
{{/returnTypeIsPrimitive}}
{{/returnType}}
],
perform: async (z, bundle) => {
{{#isMultipart}}
const formData = new FormData()
{{#allParams}}
{{#isFormParam}}
{{^isFile}}
formData.append('{{baseName}}', bundle.inputData?.['{{baseName}}'])
{{/isFile}}
{{#isFile}}
const filename = bundle.inputData?.['filename'] || bundle.inputData?.['{{baseName}}'].split('/').slice(-1)[0]
formData.append('{{baseName}}', (await (await z.request({url: bundle.inputData?.['{{baseName}}'], method: 'GET', raw: true})).buffer()), { filename: filename })
{{/isFile}}
{{/isFormParam}}
{{/allParams}}
{{/isMultipart}}
const options = {
url: utils.replacePathParameters('{{basePath}}{{path}}'),
method: '{{httpMethod}}',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{=<% %>=}}{{bundle.authData.access_token}}<%={{ }}=%>',
{{^isMultipart}}'Content-Type': '{{#consumes}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/consumes}}',{{/isMultipart}}
'Accept': '{{#produces}}{{{mediaType}}}{{^-last}}, {{/-last}}{{/produces}}',
},
params: {
{{#allParams}}
{{#isQueryParam}}
'{{baseName}}': bundle.inputData?.['{{baseName}}'],
{{/isQueryParam}}
{{/allParams}}
},
body: {{#isMultipart}}formData,{{/isMultipart}}{{^isMultipart}}{
{{#allParams}}
{{#isBodyParam}}
{{#isPrimitiveType}}'{{baseName}}': bundle.inputData?.['{{baseName}}']{{/isPrimitiveType}}{{^isPrimitiveType}}...{{baseName}}.mapping(bundle){{/isPrimitiveType}},
{{/isBodyParam}}
{{/allParams}}
},{{/isMultipart}}
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return {{#returnType}}{{#returnTypeIsPrimitive}}{ data: results }{{/returnTypeIsPrimitive}}{{^returnTypeIsPrimitive}}results{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}results{{/returnType}};
})
},
sample: {{#returnType}}{{^returnTypeIsPrimitive}}{{#responses}}{{#is2xx}}{{#baseType}}samples['{{baseType}}Sample']{{/baseType}}{{/is2xx}}{{/responses}}{{/returnTypeIsPrimitive}}{{#returnTypeIsPrimitive}}{ data: {} }{{/returnTypeIsPrimitive}}{{/returnType}}{{^returnType}}{ data: {} }{{/returnType}}
}
},
{{/operation}}
}
{{/operations}}

View File

@ -0,0 +1,4 @@
module.exports = {
// TODO: autentication logic
// https://platform.zapier.com/cli_tutorials/getting-started#adding-authentication
};

View File

@ -0,0 +1,10 @@
const authentication = require('./authentication');
const { searchActions, createActions } = require('./operations/actions');
module.exports = {
version: require('./package.json').version,
platformVersion: require('zapier-platform-core').version,
authentication: authentication,
searches: searchActions(),
creates: createActions(),
};

View File

@ -0,0 +1,93 @@
const utils = require('../utils/utils');
{{#imports}}{{{import}}}
{{/imports}}
{{#models}}
{{#model}}
{{#isEnum}}
module.exports = {
fields: (key) => (
{
label: `{{#description}}{{{.}}} - {{/description}}[${key.replaceAll('__', '.')}]`,
choices: [
{{#allowableValues}}
{{#values}}
'{{.}}',
{{/values}}
{{/allowableValues}}
],
}
)
}
{{/isEnum}}
{{^isEnum}}
module.exports = {
fields: (prefix = '', isInput = true, isArrayChild = false) => {
const {keyPrefix, labelPrefix} = utils.buildKeyAndLabel(prefix, isInput, isArrayChild)
return [
{{#vars}}
{{#isPrimitiveType}}
{
key: `${keyPrefix}{{baseName}}`,
label: `{{#description}}{{{.}}} - {{/description}}[${labelPrefix}{{baseName}}]`,
{{#isArray}}
list: true,
type: '{{#items}}{{baseType}}{{/items}}',
{{/isArray}}
{{^isArray}}
{{#isFreeFormObject}}
dict: true,
{{/isFreeFormObject}}
{{^isFreeFormObject}}
type: '{{baseType}}',
{{/isFreeFormObject}}
{{/isArray}}
{{#isEnum}}
choices: [
{{#_enum}}
'{{.}}',
{{/_enum}}
],
{{/isEnum}}
},
{{/isPrimitiveType}}
{{^isPrimitiveType}}
{{#isArray}}
{
key: `${keyPrefix}{{baseName}}`,{{#items}}{{^isEnumRef}}
label: `{{#description}}{{{.}}} - {{/description}}[${labelPrefix}{{baseName}}]`,
children: {{complexType}}.fields(`${keyPrefix}{{baseName}}${!isInput ? '[]' : ''}`, isInput, true), {{/isEnumRef}}{{#isEnumRef}}
list: true,
type: 'string',
...{{complexType}}.fields(`${keyPrefix}{{baseName}}`, isInput),{{/isEnumRef}}{{/items}}
},
{{/isArray}}
{{^isArray}}
{{^allowableValues}}
{{^isFreeFormObject}}
...{{complexType}}.fields(`${keyPrefix}{{baseName}}`, isInput),
{{/isFreeFormObject}}
{{/allowableValues}}
{{#allowableValues}}
{
key: `${keyPrefix}{{baseName}}`,
...{{complexType}}.fields(`${keyPrefix}{{baseName}}`, isInput),
},
{{/allowableValues}}
{{/isArray}}
{{/isPrimitiveType}}
{{/vars}}
]
},
mapping: (bundle, prefix = '') => {
const {keyPrefix} = utils.buildKeyAndLabel(prefix)
return {
{{#vars}}
'{{baseName}}': {{#isPrimitiveType}}bundle.inputData?.[`${keyPrefix}{{baseName}}`]{{/isPrimitiveType}}{{^isPrimitiveType}}{{^allowableValues}}{{^isArray}}utils.removeIfEmpty({{complexType}}.mapping(bundle, `${keyPrefix}{{baseName}}`)){{/isArray}}{{#isArray}}utils.removeKeyPrefixes(bundle.inputData?.[`${keyPrefix}{{baseName}}`]){{/isArray}}{{/allowableValues}}{{#allowableValues}}bundle.inputData?.[`${keyPrefix}{{baseName}}`]{{/allowableValues}}{{/isPrimitiveType}},
{{/vars}}
}
},
}
{{/isEnum}}
{{/model}}
{{/models}}

View File

@ -0,0 +1,27 @@
{
"name": "{{npmName}}",
"version": "{{npmVersion}}",
"description": "OpenAPI client for {{npmName}}",
"author": "OpenAPI-Generator",
"main": "index.js",
"scripts": {
"test": "mocha --recursive -t 10000"
},
"engines": {
"node": ">=v16",
"npm": ">=5.6.0"
},
"dependencies": {
"lodash": "^4.17.21",
"zapier-platform-core": "14.1.1",
"form-data": "2.1.4"
},
"devDependencies": {
"mocha": "^10.2.0",
"should": "^13.2.0"
},
"private": true,
"zapier": {
"convertedByCLIVersion": "14.1.1"
}
}

View File

@ -0,0 +1,18 @@
{{#operations}}
module.exports = {
{{#operation}}
{{#returnType}}
{{^returnTypeIsPrimitive}}
{{#responses}}
{{#is2xx}}
{{#baseType}}
"{{baseType}}Sample":
{{#examples}}{{#-last}}{{{example}}}{{/-last}}{{/examples}}{{^examples}}{ data: {} }{{/examples}},
{{/baseType}}
{{/is2xx}}
{{/responses}}
{{/returnTypeIsPrimitive}}
{{/returnType}}
{{/operation}}
}
{{/operations}}

View File

@ -0,0 +1,34 @@
const _ = require('lodash')
const replacePathParameters = (url) => url.replace(/{([^{}]+)}/g, (keyExpr, key) => `{{bundle.inputData.${key}}}`)
const removeKeyPrefixes = (objectsArray) => objectsArray == undefined || typeof objectsArray[0] != 'object' ? objectsArray : objectsArray.map((obj) => Object.keys(obj).reduce((res, key) => (res[(key.split('.')).slice(-1)] = obj[key], res), {}))
const removeIfEmpty = (obj) => _.isEmpty(JSON.parse(JSON.stringify(obj))) ? undefined : obj
const buildKeyAndLabel = (prefix, isInput = true, isArrayChild = false) => {
const keyPrefix = !_.isEmpty(prefix) && (!isArrayChild || isInput) ? `${prefix}${isInput ? '.' : '__'}` : prefix
const labelPrefix = !_.isEmpty(keyPrefix) ? keyPrefix.replaceAll('__', '.') : ''
return {
keyPrefix: keyPrefix,
labelPrefix:labelPrefix,
}
}
const isSearchAction = (key) => {
// TODO: custom logic
return false
}
const hasASearchField = action => action.operation.inputFields.length > 0
const returnsObjectsArray = action => !!action.operation.outputFields.find(field => 'children' in field)
const hasSearchRequisites = action => hasASearchField(action) && returnsObjectsArray(action)
const searchMiddleware = (action) => {
// TODO: custom logic
return action
}
module.exports = {
replacePathParameters: replacePathParameters,
removeKeyPrefixes: removeKeyPrefixes,
removeIfEmpty: removeIfEmpty,
buildKeyAndLabel: buildKeyAndLabel,
hasSearchRequisites: hasSearchRequisites,
isSearchAction: isSearchAction,
searchMiddleware: searchMiddleware,
}

View File

@ -2023,15 +2023,19 @@ namespace Org.OpenAPITools.Api
/// <summary> /// <summary>
/// Validates the request parameters /// Validates the request parameters
/// </summary> /// </summary>
/// <param name="enumHeaderString"></param>
/// <param name="enumHeaderStringArray"></param> /// <param name="enumHeaderStringArray"></param>
/// <param name="enumQueryStringArray"></param> /// <param name="enumQueryStringArray"></param>
/// <param name="enumFormStringArray"></param> /// <param name="enumFormStringArray"></param>
/// <param name="enumHeaderString"></param> /// <param name="enumHeaderString"></param>
/// <param name="enumQueryString"></param> /// <param name="enumFormString"></param>
/// <param name="enumQueryString"></param> /// <param name="enumQueryString"></param>
/// <param name="enumFormString"></param>
/// <returns></returns> /// <returns></returns>
private void ValidateTestEnumParameters(Option<List<string>> enumHeaderStringArray, Option<List<string>> enumQueryStringArray, Option<List<string>> enumFormStringArray, Option<string> enumHeaderString, Option<string> enumQueryString, Option<string> enumFormString) private void ValidateTestEnumParameters(Option<List<string>> enumHeaderStringArray, Option<List<string>> enumQueryStringArray, Option<List<string>> enumFormStringArray, Option<string> enumHeaderString, Option<string> enumQueryString, Option<string> enumFormString)
{ {
if (enumHeaderString.IsSet && enumHeaderString.Value == null)
throw new ArgumentNullException(nameof(enumHeaderString));
if (enumHeaderStringArray.IsSet && enumHeaderStringArray.Value == null) if (enumHeaderStringArray.IsSet && enumHeaderStringArray.Value == null)
throw new ArgumentNullException(nameof(enumHeaderStringArray)); throw new ArgumentNullException(nameof(enumHeaderStringArray));
@ -2049,6 +2053,9 @@ namespace Org.OpenAPITools.Api
if (enumFormString.IsSet && enumFormString.Value == null) if (enumFormString.IsSet && enumFormString.Value == null)
throw new ArgumentNullException(nameof(enumFormString)); throw new ArgumentNullException(nameof(enumFormString));
if (enumQueryString.IsSet && enumQueryString.Value == null)
throw new ArgumentNullException(nameof(enumQueryString));
} }
/// <summary> /// <summary>

View File

@ -2042,11 +2042,15 @@ namespace Org.OpenAPITools.Api
if (enumHeaderString.IsSet && enumHeaderString.Value == null) if (enumHeaderString.IsSet && enumHeaderString.Value == null)
throw new ArgumentNullException(nameof(enumHeaderString)); throw new ArgumentNullException(nameof(enumHeaderString));
if (enumQueryString.IsSet && enumQueryString.Value == null) if (enumQueryString.IsSet && enumQueryString.Value == null)
throw new ArgumentNullException(nameof(enumQueryString)); throw new ArgumentNullException(nameof(enumQueryString));
if (enumFormString.IsSet && enumFormString.Value == null) if (enumFormString.IsSet && enumFormString.Value == null)
throw new ArgumentNullException(nameof(enumFormString)); throw new ArgumentNullException(nameof(enumFormString));
if (enumQueryString.IsSet && enumQueryString.Value == null)
throw new ArgumentNullException(nameof(enumQueryString));
} }
/// <summary> /// <summary>

View File

@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.
# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md

View File

@ -0,0 +1,15 @@
README.md
apis/PetApi.js
apis/StoreApi.js
apis/UserApi.js
authentication.js
index.js
models/ApiResponse.js
models/Category.js
models/Order.js
models/Pet.js
models/Tag.js
models/User.js
operations/actions.js
package.json
utils/utils.js

View File

@ -0,0 +1 @@
7.0.0-SNAPSHOT

View File

@ -0,0 +1,51 @@
# Documentation for zapier generator
This generator generates a partial implementation of a zapier integration from an openapi specification.
## Why is it partial?
It's a partial integration because you have to put in some code for it to be 100% complete, code that depends only on how your api is structured.
Here there are all the parts you need to complete by yourself:
### 1) **utils/utils.js** isSearchAction method
This method has to return either true or false if a method is a [zapier search action](https://platform.zapier.com/docs/search-create).
```
const isSearchAction = (key) => {
// TODO: custom logic
return false
}
```
### 2) **utils/utils.js** searchMiddleware method
This method has to return an array of resources (searches must return an array), if you have pagination or you api returns an array inside nested fields, here you have to extract the array.
```
const searchMiddleware = (action) => {
// TODO: custom logic
return action
}
```
### 3) **authentication.js**
This file must be written completely according to your api authentication, you can get it generated automatically by creating the integration on the zapier website and filling the requested data, or you can build it yourself following [this guide](https://platform.zapier.com/cli_tutorials/getting-started#adding-authentication).
## Samples
To get your app made public on zapier you must provide a sample (json example response) for each of your actions, these samples get automatically generated by this generator but you have to have actual response examples in you openapi file.
For example:
```
CreateUserResponse:
description: Example response
content:
application/json:
schema:
$ref: ./models/responses/CreateUserResponse.yaml
examples:
example-1:
value:
data:
id: 12345
name: user1
```

View File

@ -0,0 +1,370 @@
const samples = require('../samples/PetApi');
const ApiResponse = require('../models/ApiResponse');
const Pet = require('../models/Pet');
const utils = require('../utils/utils');
const FormData = require('form-data');
module.exports = {
addPet: {
key: 'addPet',
noun: 'Pet',
display: {
label: 'addPet',
description: '',
hidden: false,
},
operation: {
inputFields: [
...Pet.fields(),
],
outputFields: [
...Pet.fields('', false),
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/pet'),
method: 'POST',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': 'application/json, application/xml',
'Accept': 'application/xml, application/json',
},
params: {
},
body: {
...Pet.mapping(bundle),
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: samples['PetSample']
}
},
deletePet: {
key: 'deletePet',
noun: 'Pet',
display: {
label: 'deletePet',
description: '',
hidden: false,
},
operation: {
inputFields: [
{
key: 'petId',
label: 'Pet id to delete',
type: '',
required: true,
},
{
key: 'api_key',
label: '',
type: 'string',
},
],
outputFields: [
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/pet/{petId}'),
method: 'DELETE',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': '',
'Accept': '',
},
params: {
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: { data: {} }
}
},
findPetsByStatus: {
key: 'findPetsByStatus',
noun: 'Pet',
display: {
label: 'findPetsByStatus',
description: 'Multiple status values can be provided with comma separated strings',
hidden: false,
},
operation: {
inputFields: [
...string.fields(),
],
outputFields: [
...array.fields('', false),
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/pet/findByStatus'),
method: 'GET',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': '',
'Accept': 'application/xml, application/json',
},
params: {
'status': bundle.inputData?.['status'],
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: samples['PetSample']
}
},
findPetsByTags: {
key: 'findPetsByTags',
noun: 'Pet',
display: {
label: 'findPetsByTags',
description: 'Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.',
hidden: false,
},
operation: {
inputFields: [
...string.fields(),
],
outputFields: [
...array.fields('', false),
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/pet/findByTags'),
method: 'GET',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': '',
'Accept': 'application/xml, application/json',
},
params: {
'tags': bundle.inputData?.['tags'],
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: samples['PetSample']
}
},
getPetById: {
key: 'getPetById',
noun: 'Pet',
display: {
label: 'getPetById',
description: 'Returns a single pet',
hidden: false,
},
operation: {
inputFields: [
{
key: 'petId',
label: 'ID of pet to return',
type: '',
required: true,
},
],
outputFields: [
...Pet.fields('', false),
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/pet/{petId}'),
method: 'GET',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': '',
'Accept': 'application/xml, application/json',
},
params: {
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: samples['PetSample']
}
},
updatePet: {
key: 'updatePet',
noun: 'Pet',
display: {
label: 'updatePet',
description: '',
hidden: false,
},
operation: {
inputFields: [
...Pet.fields(),
],
outputFields: [
...Pet.fields('', false),
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/pet'),
method: 'PUT',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': 'application/json, application/xml',
'Accept': 'application/xml, application/json',
},
params: {
},
body: {
...Pet.mapping(bundle),
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: samples['PetSample']
}
},
updatePetWithForm: {
key: 'updatePetWithForm',
noun: 'Pet',
display: {
label: 'updatePetWithForm',
description: '',
hidden: false,
},
operation: {
inputFields: [
{
key: 'petId',
label: 'ID of pet that needs to be updated',
type: '',
required: true,
},
{
key: 'name',
label: 'Updated name of the pet',
type: 'string',
},
{
key: 'status',
label: 'Updated status of the pet',
type: 'string',
},
],
outputFields: [
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/pet/{petId}'),
method: 'POST',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': '',
},
params: {
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: { data: {} }
}
},
uploadFile: {
key: 'uploadFile',
noun: 'Pet',
display: {
label: 'uploadFile',
description: '',
hidden: false,
},
operation: {
inputFields: [
{
key: 'petId',
label: 'ID of pet to update',
type: '',
required: true,
},
{
key: 'additionalMetadata',
label: 'Additional data to pass to server',
type: 'string',
},
{
key: 'file',
label: 'file to upload',
type: 'file',
},
],
outputFields: [
...ApiResponse.fields('', false),
],
perform: async (z, bundle) => {
const formData = new FormData()
formData.append('additionalMetadata', bundle.inputData?.['additionalMetadata'])
const filename = bundle.inputData?.['filename'] || bundle.inputData?.['file'].split('/').slice(-1)[0]
formData.append('file', (await (await z.request({url: bundle.inputData?.['file'], method: 'GET', raw: true})).buffer()), { filename: filename })
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/pet/{petId}/uploadImage'),
method: 'POST',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Accept': 'application/json',
},
params: {
},
body: formData,
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: samples['ApiResponseSample']
}
},
}

View File

@ -0,0 +1,170 @@
const samples = require('../samples/StoreApi');
const Order = require('../models/Order');
const utils = require('../utils/utils');
module.exports = {
deleteOrder: {
key: 'deleteOrder',
noun: 'Store',
display: {
label: 'deleteOrder',
description: 'For valid response try integer IDs with value &lt; 1000. Anything above 1000 or nonintegers will generate API errors',
hidden: false,
},
operation: {
inputFields: [
{
key: 'orderId',
label: 'ID of the order that needs to be deleted',
type: 'string',
required: true,
},
],
outputFields: [
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/store/order/{orderId}'),
method: 'DELETE',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': '',
'Accept': '',
},
params: {
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: { data: {} }
}
},
getInventory: {
key: 'getInventory',
noun: 'Store',
display: {
label: 'getInventory',
description: 'Returns a map of status codes to quantities',
hidden: false,
},
operation: {
inputFields: [
],
outputFields: [
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/store/inventory'),
method: 'GET',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': '',
'Accept': 'application/json',
},
params: {
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return { data: results };
})
},
sample: { data: {} }
}
},
getOrderById: {
key: 'getOrderById',
noun: 'Store',
display: {
label: 'getOrderById',
description: 'For valid response try integer IDs with value &lt;&#x3D; 5 or &gt; 10. Other values will generate exceptions',
hidden: false,
},
operation: {
inputFields: [
{
key: 'orderId',
label: 'ID of pet that needs to be fetched',
type: '',
required: true,
},
],
outputFields: [
...Order.fields('', false),
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/store/order/{orderId}'),
method: 'GET',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': '',
'Accept': 'application/xml, application/json',
},
params: {
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: samples['OrderSample']
}
},
placeOrder: {
key: 'placeOrder',
noun: 'Store',
display: {
label: 'placeOrder',
description: '',
hidden: false,
},
operation: {
inputFields: [
...Order.fields(),
],
outputFields: [
...Order.fields('', false),
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/store/order'),
method: 'POST',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': 'application/json',
'Accept': 'application/xml, application/json',
},
params: {
},
body: {
...Order.mapping(bundle),
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: samples['OrderSample']
}
},
}

View File

@ -0,0 +1,343 @@
const samples = require('../samples/UserApi');
const User = require('../models/User');
const utils = require('../utils/utils');
module.exports = {
createUser: {
key: 'createUser',
noun: 'User',
display: {
label: 'createUser',
description: 'This can only be done by the logged in user.',
hidden: false,
},
operation: {
inputFields: [
...User.fields(),
],
outputFields: [
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/user'),
method: 'POST',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': 'application/json',
'Accept': '',
},
params: {
},
body: {
...User.mapping(bundle),
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: { data: {} }
}
},
createUsersWithArrayInput: {
key: 'createUsersWithArrayInput',
noun: 'User',
display: {
label: 'createUsersWithArrayInput',
description: '',
hidden: false,
},
operation: {
inputFields: [
...User.fields(),
],
outputFields: [
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/user/createWithArray'),
method: 'POST',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': 'application/json',
'Accept': '',
},
params: {
},
body: {
...User.mapping(bundle),
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: { data: {} }
}
},
createUsersWithListInput: {
key: 'createUsersWithListInput',
noun: 'User',
display: {
label: 'createUsersWithListInput',
description: '',
hidden: false,
},
operation: {
inputFields: [
...User.fields(),
],
outputFields: [
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/user/createWithList'),
method: 'POST',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': 'application/json',
'Accept': '',
},
params: {
},
body: {
...User.mapping(bundle),
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: { data: {} }
}
},
deleteUser: {
key: 'deleteUser',
noun: 'User',
display: {
label: 'deleteUser',
description: 'This can only be done by the logged in user.',
hidden: false,
},
operation: {
inputFields: [
{
key: 'username',
label: 'The name that needs to be deleted',
type: 'string',
required: true,
},
],
outputFields: [
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/user/{username}'),
method: 'DELETE',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': '',
'Accept': '',
},
params: {
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: { data: {} }
}
},
getUserByName: {
key: 'getUserByName',
noun: 'User',
display: {
label: 'getUserByName',
description: '',
hidden: false,
},
operation: {
inputFields: [
{
key: 'username',
label: 'The name that needs to be fetched. Use user1 for testing.',
type: 'string',
required: true,
},
],
outputFields: [
...User.fields('', false),
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/user/{username}'),
method: 'GET',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': '',
'Accept': 'application/xml, application/json',
},
params: {
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: samples['UserSample']
}
},
loginUser: {
key: 'loginUser',
noun: 'User',
display: {
label: 'loginUser',
description: '',
hidden: false,
},
operation: {
inputFields: [
{
key: 'username',
label: 'The user name for login',
type: 'string',
required: true,
},
{
key: 'password',
label: 'The password for login in clear text',
type: 'string',
required: true,
},
],
outputFields: [
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/user/login'),
method: 'GET',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': '',
'Accept': 'application/xml, application/json',
},
params: {
'username': bundle.inputData?.['username'],
'password': bundle.inputData?.['password'],
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return { data: results };
})
},
sample: { data: {} }
}
},
logoutUser: {
key: 'logoutUser',
noun: 'User',
display: {
label: 'logoutUser',
description: '',
hidden: false,
},
operation: {
inputFields: [
],
outputFields: [
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/user/logout'),
method: 'GET',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': '',
'Accept': '',
},
params: {
},
body: {
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: { data: {} }
}
},
updateUser: {
key: 'updateUser',
noun: 'User',
display: {
label: 'updateUser',
description: 'This can only be done by the logged in user.',
hidden: false,
},
operation: {
inputFields: [
{
key: 'username',
label: 'name that need to be deleted',
type: 'string',
required: true,
},
...User.fields(),
],
outputFields: [
],
perform: async (z, bundle) => {
const options = {
url: utils.replacePathParameters('http://petstore.swagger.io/v2/user/{username}'),
method: 'PUT',
removeMissingValuesFrom: { params: true, body: true },
headers: {
'Authorization': 'Bearer {{bundle.authData.access_token}}',
'Content-Type': 'application/json',
'Accept': '',
},
params: {
},
body: {
...User.mapping(bundle),
},
}
return z.request(options).then((response) => {
response.throwForStatus();
const results = response.json;
return results;
})
},
sample: { data: {} }
}
},
}

View File

@ -0,0 +1,4 @@
module.exports = {
// TODO: autentication logic
// https://platform.zapier.com/cli_tutorials/getting-started#adding-authentication
};

View File

@ -0,0 +1,10 @@
const authentication = require('./authentication');
const { searchActions, createActions } = require('./operations/actions');
module.exports = {
version: require('./package.json').version,
platformVersion: require('zapier-platform-core').version,
authentication: authentication,
searches: searchActions(),
creates: createActions(),
};

View File

@ -0,0 +1,32 @@
const utils = require('../utils/utils');
module.exports = {
fields: (prefix = '', isInput = true, isArrayChild = false) => {
const {keyPrefix, labelPrefix} = utils.buildKeyAndLabel(prefix, isInput, isArrayChild)
return [
{
key: `${keyPrefix}code`,
label: `[${labelPrefix}code]`,
type: 'integer',
},
{
key: `${keyPrefix}type`,
label: `[${labelPrefix}type]`,
type: 'string',
},
{
key: `${keyPrefix}message`,
label: `[${labelPrefix}message]`,
type: 'string',
},
]
},
mapping: (bundle, prefix = '') => {
const {keyPrefix} = utils.buildKeyAndLabel(prefix)
return {
'code': bundle.inputData?.[`${keyPrefix}code`],
'type': bundle.inputData?.[`${keyPrefix}type`],
'message': bundle.inputData?.[`${keyPrefix}message`],
}
},
}

View File

@ -0,0 +1,26 @@
const utils = require('../utils/utils');
module.exports = {
fields: (prefix = '', isInput = true, isArrayChild = false) => {
const {keyPrefix, labelPrefix} = utils.buildKeyAndLabel(prefix, isInput, isArrayChild)
return [
{
key: `${keyPrefix}id`,
label: `[${labelPrefix}id]`,
type: 'number',
},
{
key: `${keyPrefix}name`,
label: `[${labelPrefix}name]`,
type: 'string',
},
]
},
mapping: (bundle, prefix = '') => {
const {keyPrefix} = utils.buildKeyAndLabel(prefix)
return {
'id': bundle.inputData?.[`${keyPrefix}id`],
'name': bundle.inputData?.[`${keyPrefix}name`],
}
},
}

View File

@ -0,0 +1,55 @@
const utils = require('../utils/utils');
module.exports = {
fields: (prefix = '', isInput = true, isArrayChild = false) => {
const {keyPrefix, labelPrefix} = utils.buildKeyAndLabel(prefix, isInput, isArrayChild)
return [
{
key: `${keyPrefix}id`,
label: `[${labelPrefix}id]`,
type: 'number',
},
{
key: `${keyPrefix}petId`,
label: `[${labelPrefix}petId]`,
type: 'number',
},
{
key: `${keyPrefix}quantity`,
label: `[${labelPrefix}quantity]`,
type: 'integer',
},
{
key: `${keyPrefix}shipDate`,
label: `[${labelPrefix}shipDate]`,
type: 'string',
},
{
key: `${keyPrefix}status`,
label: `Order Status - [${labelPrefix}status]`,
type: 'string',
choices: [
'placed',
'approved',
'delivered',
],
},
{
key: `${keyPrefix}complete`,
label: `[${labelPrefix}complete]`,
type: 'boolean',
},
]
},
mapping: (bundle, prefix = '') => {
const {keyPrefix} = utils.buildKeyAndLabel(prefix)
return {
'id': bundle.inputData?.[`${keyPrefix}id`],
'petId': bundle.inputData?.[`${keyPrefix}petId`],
'quantity': bundle.inputData?.[`${keyPrefix}quantity`],
'shipDate': bundle.inputData?.[`${keyPrefix}shipDate`],
'status': bundle.inputData?.[`${keyPrefix}status`],
'complete': bundle.inputData?.[`${keyPrefix}complete`],
}
},
}

View File

@ -0,0 +1,54 @@
const utils = require('../utils/utils');
const Category = require('../models/Category');
const Tag = require('../models/Tag');
module.exports = {
fields: (prefix = '', isInput = true, isArrayChild = false) => {
const {keyPrefix, labelPrefix} = utils.buildKeyAndLabel(prefix, isInput, isArrayChild)
return [
{
key: `${keyPrefix}id`,
label: `[${labelPrefix}id]`,
type: 'number',
},
...Category.fields(`${keyPrefix}category`, isInput),
{
key: `${keyPrefix}name`,
label: `[${labelPrefix}name]`,
type: 'string',
},
{
key: `${keyPrefix}photoUrls`,
label: `[${labelPrefix}photoUrls]`,
list: true,
type: 'string',
},
{
key: `${keyPrefix}tags`,
label: `[${labelPrefix}tags]`,
children: Tag.fields(`${keyPrefix}tags${!isInput ? '[]' : ''}`, isInput, true),
},
{
key: `${keyPrefix}status`,
label: `pet status in the store - [${labelPrefix}status]`,
type: 'string',
choices: [
'available',
'pending',
'sold',
],
},
]
},
mapping: (bundle, prefix = '') => {
const {keyPrefix} = utils.buildKeyAndLabel(prefix)
return {
'id': bundle.inputData?.[`${keyPrefix}id`],
'category': utils.removeIfEmpty(Category.mapping(bundle, `${keyPrefix}category`)),
'name': bundle.inputData?.[`${keyPrefix}name`],
'photoUrls': bundle.inputData?.[`${keyPrefix}photoUrls`],
'tags': utils.removeKeyPrefixes(bundle.inputData?.[`${keyPrefix}tags`]),
'status': bundle.inputData?.[`${keyPrefix}status`],
}
},
}

View File

@ -0,0 +1,26 @@
const utils = require('../utils/utils');
module.exports = {
fields: (prefix = '', isInput = true, isArrayChild = false) => {
const {keyPrefix, labelPrefix} = utils.buildKeyAndLabel(prefix, isInput, isArrayChild)
return [
{
key: `${keyPrefix}id`,
label: `[${labelPrefix}id]`,
type: 'number',
},
{
key: `${keyPrefix}name`,
label: `[${labelPrefix}name]`,
type: 'string',
},
]
},
mapping: (bundle, prefix = '') => {
const {keyPrefix} = utils.buildKeyAndLabel(prefix)
return {
'id': bundle.inputData?.[`${keyPrefix}id`],
'name': bundle.inputData?.[`${keyPrefix}name`],
}
},
}

View File

@ -0,0 +1,62 @@
const utils = require('../utils/utils');
module.exports = {
fields: (prefix = '', isInput = true, isArrayChild = false) => {
const {keyPrefix, labelPrefix} = utils.buildKeyAndLabel(prefix, isInput, isArrayChild)
return [
{
key: `${keyPrefix}id`,
label: `[${labelPrefix}id]`,
type: 'number',
},
{
key: `${keyPrefix}username`,
label: `[${labelPrefix}username]`,
type: 'string',
},
{
key: `${keyPrefix}firstName`,
label: `[${labelPrefix}firstName]`,
type: 'string',
},
{
key: `${keyPrefix}lastName`,
label: `[${labelPrefix}lastName]`,
type: 'string',
},
{
key: `${keyPrefix}email`,
label: `[${labelPrefix}email]`,
type: 'string',
},
{
key: `${keyPrefix}password`,
label: `[${labelPrefix}password]`,
type: 'string',
},
{
key: `${keyPrefix}phone`,
label: `[${labelPrefix}phone]`,
type: 'string',
},
{
key: `${keyPrefix}userStatus`,
label: `User Status - [${labelPrefix}userStatus]`,
type: 'integer',
},
]
},
mapping: (bundle, prefix = '') => {
const {keyPrefix} = utils.buildKeyAndLabel(prefix)
return {
'id': bundle.inputData?.[`${keyPrefix}id`],
'username': bundle.inputData?.[`${keyPrefix}username`],
'firstName': bundle.inputData?.[`${keyPrefix}firstName`],
'lastName': bundle.inputData?.[`${keyPrefix}lastName`],
'email': bundle.inputData?.[`${keyPrefix}email`],
'password': bundle.inputData?.[`${keyPrefix}password`],
'phone': bundle.inputData?.[`${keyPrefix}phone`],
'userStatus': bundle.inputData?.[`${keyPrefix}userStatus`],
}
},
}

View File

@ -0,0 +1,32 @@
const PetApi = require('../apis/PetApi');
const StoreApi = require('../apis/StoreApi');
const UserApi = require('../apis/UserApi');
const { searchMiddleware, hasSearchRequisites, isSearchAction } = require('../utils/utils');
const actions = {
[PetApi.addPet.key]: PetApi.addPet,
[PetApi.deletePet.key]: PetApi.deletePet,
[PetApi.findPetsByStatus.key]: PetApi.findPetsByStatus,
[PetApi.findPetsByTags.key]: PetApi.findPetsByTags,
[PetApi.getPetById.key]: PetApi.getPetById,
[PetApi.updatePet.key]: PetApi.updatePet,
[PetApi.updatePetWithForm.key]: PetApi.updatePetWithForm,
[PetApi.uploadFile.key]: PetApi.uploadFile,
[StoreApi.deleteOrder.key]: StoreApi.deleteOrder,
[StoreApi.getInventory.key]: StoreApi.getInventory,
[StoreApi.getOrderById.key]: StoreApi.getOrderById,
[StoreApi.placeOrder.key]: StoreApi.placeOrder,
[UserApi.createUser.key]: UserApi.createUser,
[UserApi.createUsersWithArrayInput.key]: UserApi.createUsersWithArrayInput,
[UserApi.createUsersWithListInput.key]: UserApi.createUsersWithListInput,
[UserApi.deleteUser.key]: UserApi.deleteUser,
[UserApi.getUserByName.key]: UserApi.getUserByName,
[UserApi.loginUser.key]: UserApi.loginUser,
[UserApi.logoutUser.key]: UserApi.logoutUser,
[UserApi.updateUser.key]: UserApi.updateUser,
}
module.exports = {
searchActions: () => Object.entries(actions).reduce((actions, [key, value]) => isSearchAction(key) && hasSearchRequisites(value) ? {...actions, [key]: searchMiddleware(value)} : actions, {}),
createActions: () => Object.entries(actions).reduce((actions, [key, value]) => !isSearchAction(key) ? {...actions, [key]: value} : actions, {}),
}

View File

@ -0,0 +1,27 @@
{
"name": "@openapitools/zapier",
"version": "0.0.1",
"description": "OpenAPI client for @openapitools/zapier",
"author": "OpenAPI-Generator",
"main": "index.js",
"scripts": {
"test": "mocha --recursive -t 10000"
},
"engines": {
"node": ">=v16",
"npm": ">=5.6.0"
},
"dependencies": {
"lodash": "^4.17.21",
"zapier-platform-core": "14.1.1",
"form-data": "2.1.4"
},
"devDependencies": {
"mocha": "^10.2.0",
"should": "^13.2.0"
},
"private": true,
"zapier": {
"convertedByCLIVersion": "14.1.1"
}
}

View File

@ -0,0 +1,14 @@
module.export = {
"PetSample":
{ data: {} },
"PetSample":
{ data: {} },
"PetSample":
{ data: {} },
"PetSample":
{ data: {} },
"PetSample":
{ data: {} },
"ApiResponseSample":
{ data: {} },
}

View File

@ -0,0 +1,6 @@
module.export = {
"OrderSample":
{ data: {} },
"OrderSample":
{ data: {} },
}

View File

@ -0,0 +1,4 @@
module.export = {
"UserSample":
{ data: {} },
}

View File

@ -0,0 +1,34 @@
const _ = require('lodash')
const replacePathParameters = (url) => url.replace(/{([^{}]+)}/g, (keyExpr, key) => `{{bundle.inputData.${key}}}`)
const removeKeyPrefixes = (objectsArray) => objectsArray == undefined || typeof objectsArray[0] != 'object' ? objectsArray : objectsArray.map((obj) => Object.keys(obj).reduce((res, key) => (res[(key.split('.')).slice(-1)] = obj[key], res), {}))
const removeIfEmpty = (obj) => _.isEmpty(JSON.parse(JSON.stringify(obj))) ? undefined : obj
const buildKeyAndLabel = (prefix, isInput = true, isArrayChild = false) => {
const keyPrefix = !_.isEmpty(prefix) && (!isArrayChild || isInput) ? `${prefix}${isInput ? '.' : '__'}` : prefix
const labelPrefix = !_.isEmpty(keyPrefix) ? keyPrefix.replaceAll('__', '.') : ''
return {
keyPrefix: keyPrefix,
labelPrefix:labelPrefix,
}
}
const isSearchAction = (key) => {
// TODO: custom logic
return false
}
const hasASearchField = action => action.operation.inputFields.length > 0
const returnsObjectsArray = action => !!action.operation.outputFields.find(field => 'children' in field)
const hasSearchRequisites = action => hasASearchField(action) && returnsObjectsArray(action)
const searchMiddleware = (action) => {
// TODO: custom logic
return action
}
module.exports = {
replacePathParameters: replacePathParameters,
removeKeyPrefixes: removeKeyPrefixes,
removeIfEmpty: removeIfEmpty,
buildKeyAndLabel: buildKeyAndLabel,
hasSearchRequisites: hasSearchRequisites,
isSearchAction: isSearchAction,
searchMiddleware: searchMiddleware,
}