Skip to content

Commit

Permalink
update codegen
Browse files Browse the repository at this point in the history
  • Loading branch information
eluce2 committed Aug 9, 2023
1 parent 520ade5 commit 30aa8a9
Show file tree
Hide file tree
Showing 3 changed files with 187 additions and 138 deletions.
6 changes: 6 additions & 0 deletions .changeset/strange-swans-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@proofgeist/fmdapi": minor
---

Add WebViewer Client
You can now use easily use this package with FileMaker webviewer integrations! Simply add @proofgeist/fm-webviewer-fetch to your project and specify the FM Script Name that runs the Execute Data API command in the fmschema.config file. Now you'll have autogenerated types for your FileMaker layouts but without sending calls via the network!
311 changes: 174 additions & 137 deletions src/utils/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,22 +96,23 @@ const exportIndexClientStatement = (schemaName: string) =>
undefined
);

const importStatement = factory.createImportDeclaration(
undefined,
factory.createImportClause(
false,
const importStatement = (wv = false) =>
factory.createImportDeclaration(
undefined,
factory.createNamedImports([
factory.createImportSpecifier(
false,
undefined,
factory.createIdentifier("DataApi")
),
])
),
factory.createStringLiteral("@proofgeist/fmdapi"),
undefined
);
factory.createImportClause(
false,
undefined,
factory.createNamedImports([
factory.createImportSpecifier(
false,
wv ? factory.createIdentifier("DataApiWv") : undefined,
factory.createIdentifier("DataApi")
),
])
),
factory.createStringLiteral("@proofgeist/fmdapi"),
undefined
);
const undefinedTypeGuardStatement = (name: string) =>
factory.createIfStatement(
factory.createPrefixUnaryExpression(
Expand Down Expand Up @@ -141,8 +142,9 @@ const exportClientStatement = (args: {
useZod: boolean;
envNames: Omit<ClientObjectProps, "layout" | "tokenStore">;
tokenStore?: ts.Expression;
webviewerScriptName?: string;
}) => [
importStatement,
importStatement(args.webviewerScriptName !== undefined),
undefinedTypeGuardStatement(args.envNames.db),
undefinedTypeGuardStatement(args.envNames.server),
...(isOttoAuth(args.envNames.auth)
Expand Down Expand Up @@ -180,85 +182,105 @@ const exportClientStatement = (args: {
[
factory.createObjectLiteralExpression(
[
factory.createPropertyAssignment(
factory.createIdentifier("auth"),
factory.createObjectLiteralExpression(
isOttoAuth(args.envNames.auth)
? [
factory.createPropertyAssignment(
factory.createIdentifier("apiKey"),
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("process"),
factory.createIdentifier("env")
),
factory.createIdentifier(
args.envNames.auth.apiKey
)
)
),
]
: [
factory.createPropertyAssignment(
factory.createIdentifier("username"),
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("process"),
factory.createIdentifier("env")
),
factory.createIdentifier(
args.envNames.auth.username
)
)
...(args.webviewerScriptName !== undefined
? []
: [
factory.createPropertyAssignment(
factory.createIdentifier("auth"),
factory.createObjectLiteralExpression(
isOttoAuth(args.envNames.auth)
? [
factory.createPropertyAssignment(
factory.createIdentifier("apiKey"),
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("process"),
factory.createIdentifier("env")
),
factory.createIdentifier(
args.envNames.auth.apiKey
)
)
),
]
: [
factory.createPropertyAssignment(
factory.createIdentifier("username"),
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("process"),
factory.createIdentifier("env")
),
factory.createIdentifier(
args.envNames.auth.username
)
)
),
factory.createPropertyAssignment(
factory.createIdentifier("password"),
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("process"),
factory.createIdentifier("env")
),
factory.createIdentifier(
args.envNames.auth.password
)
)
),
],
false
)
),
]),
...(args.webviewerScriptName !== undefined
? []
: [
factory.createPropertyAssignment(
factory.createIdentifier("db"),
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("process"),
factory.createIdentifier("env")
),
factory.createPropertyAssignment(
factory.createIdentifier("password"),
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("process"),
factory.createIdentifier("env")
),
factory.createIdentifier(
args.envNames.auth.password
)
)
factory.createIdentifier(args.envNames.db)
)
),
]),
...(args.webviewerScriptName !== undefined
? []
: [
factory.createPropertyAssignment(
factory.createIdentifier("server"),
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("process"),
factory.createIdentifier("env")
),
],
false
)
),
factory.createPropertyAssignment(
factory.createIdentifier("db"),
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("process"),
factory.createIdentifier("env")
),
factory.createIdentifier(args.envNames.db)
)
),
factory.createPropertyAssignment(
factory.createIdentifier("server"),
factory.createPropertyAccessExpression(
factory.createPropertyAccessExpression(
factory.createIdentifier("process"),
factory.createIdentifier("env")
),
factory.createIdentifier(args.envNames.server)
)
),
factory.createIdentifier(args.envNames.server)
)
),
]),
factory.createPropertyAssignment(
factory.createIdentifier("layout"),
factory.createStringLiteral(args.layout)
),
...(args.tokenStore
...(args.tokenStore && args.webviewerScriptName === undefined
? [
factory.createPropertyAssignment(
factory.createIdentifier("tokenStore"),
args.tokenStore
),
]
: []),
...(args.webviewerScriptName !== undefined
? [
factory.createPropertyAssignment(
factory.createIdentifier("scriptName"),
factory.createStringLiteral(args.webviewerScriptName)
),
]
: []),
],
true
),
Expand Down Expand Up @@ -575,6 +597,7 @@ type BuildSchemaArgs = {
layoutName: string;
strictNumbers?: boolean;
configLocation?: string;
webviewerScriptName?: string;
} & Pick<GenerateSchemaOptions, "tokenStore">;
const buildClientFile = (args: BuildSchemaArgs) => {
const printer = createPrinter({ newLine: ts.NewLineKind.LineFeed });
Expand Down Expand Up @@ -612,6 +635,7 @@ const buildClient = (args: BuildSchemaArgs) => {
...(portalSchema.length > 0
? { portalTypeName: `T${varname(schemaName)}Portals` }
: {}),
webviewerScriptName: args.webviewerScriptName,
}),
]
);
Expand Down Expand Up @@ -934,6 +958,16 @@ export type GenerateSchemaOptions = {
path?: string;
useZod?: boolean;
tokenStore?: () => TokenStoreDefinitions;
/**
* If set, the generated files will include the webviewer client instead of the standard REST API client.
* This script should pass the parameter to the Execute Data API Script step and return the result to the webviewer per the "@proofgeist/fm-webviewer-fetch" documentation.
* Requires "@proofgeist/fm-webviewer-fetch" installed as a peer dependency.
* The REST API client (and related credentials) is still needed to generate the types.
*
* @default false
* @link https://fm-webviewer-fetch.proofgeist.com/
*/
webviewerScriptName?: boolean;
};
export const generateSchemas = async (
options: GenerateSchemaOptions,
Expand All @@ -945,6 +979,7 @@ export const generateSchemas = async (
path = "schema",
useZod = true,
generateClient = true,
webviewerScriptName,
} = options;

const defaultEnvNames = {
Expand All @@ -956,6 +991,13 @@ export const generateSchemas = async (
db: "FM_DATABASE",
};

if (webviewerScriptName !== undefined && !!options.tokenStore)
console.log(
`${chalk.yellow(
"NOTE:"
)} The webviewer client does not store any tokens. The tokenStore option will be ignored.`
);

// if (configLocation) {
// getTokenStoreFromConfig(configLocation);
// return;
Expand Down Expand Up @@ -1017,62 +1059,57 @@ export const generateSchemas = async (
await fs.ensureDir(path);
const clientExportsMap: { [key: string]: ts.ExportDeclaration } = {};

await Promise.all(
schemas.map(async (item) => {
const result = await getSchema({
client,
layout: item.layout,
valueLists: item.valueLists,
});
if (result) {
const { schema, portalSchema, valueLists } = result;
const args: BuildSchemaArgs = {
schemaName: item.schemaName,
schema,
layoutName: item.layout,
portalSchema,
valueLists,
type: useZod ? "zod" : "ts",
strictNumbers: item.strictNumbers,
configLocation,
envNames: {
auth: isOttoAuth(auth)
? {
apiKey:
envNames?.auth && "apiKey" in envNames.auth
? envNames.auth.apiKey
: defaultEnvNames.apiKey,
}
: {
username:
envNames?.auth && "username" in envNames.auth
? envNames.auth.username
: defaultEnvNames.username,
password:
envNames?.auth && "password" in envNames.auth
? envNames.auth.password
: defaultEnvNames.password,
},
db: envNames?.db ?? defaultEnvNames.db,
server: envNames?.server ?? defaultEnvNames.server,
},
};
const code = buildSchema(args);
fs.writeFile(join(path, `${item.schemaName}.ts`), code);
for await (const item of schemas) {
const result = await getSchema({
client,
layout: item.layout,
valueLists: item.valueLists,
});
if (!result) continue;

if (item.generateClient ?? generateClient) {
await ensureDir(join(path, "client"));
const clientCode = buildClientFile(args);
const clientExport = exportIndexClientStatement(item.schemaName);
clientExportsMap[item.schemaName] = clientExport;
fs.writeFile(
join(path, "client", `${item.schemaName}.ts`),
clientCode
);
}
}
})
);
const { schema, portalSchema, valueLists } = result;
const args: BuildSchemaArgs = {
schemaName: item.schemaName,
schema,
layoutName: item.layout,
portalSchema,
valueLists,
type: useZod ? "zod" : "ts",
strictNumbers: item.strictNumbers,
configLocation,
envNames: {
auth: isOttoAuth(auth)
? {
apiKey:
envNames?.auth && "apiKey" in envNames.auth
? envNames.auth.apiKey
: defaultEnvNames.apiKey,
}
: {
username:
envNames?.auth && "username" in envNames.auth
? envNames.auth.username
: defaultEnvNames.username,
password:
envNames?.auth && "password" in envNames.auth
? envNames.auth.password
: defaultEnvNames.password,
},
db: envNames?.db ?? defaultEnvNames.db,
server: envNames?.server ?? defaultEnvNames.server,
},
};
const code = buildSchema(args);
fs.writeFile(join(path, `${item.schemaName}.ts`), code);

if (item.generateClient ?? generateClient) {
await ensureDir(join(path, "client"));
const clientCode = buildClientFile(args);
const clientExport = exportIndexClientStatement(item.schemaName);
clientExportsMap[item.schemaName] = clientExport;
fs.writeFile(join(path, "client", `${item.schemaName}.ts`), clientCode);
}
}

if (Object.keys(clientExportsMap).length !== 0) {
// add an index file with all clients exported, sorted by name
Expand Down
Loading

0 comments on commit 30aa8a9

Please sign in to comment.