swallowkit 1.0.0-beta.46 → 1.0.0-beta.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/add-auth.d.ts +3 -0
- package/dist/cli/commands/add-auth.d.ts.map +1 -1
- package/dist/cli/commands/add-auth.js +350 -17
- package/dist/cli/commands/add-auth.js.map +1 -1
- package/dist/cli/commands/dev.d.ts +4 -0
- package/dist/cli/commands/dev.d.ts.map +1 -1
- package/dist/cli/commands/dev.js +104 -13
- package/dist/cli/commands/dev.js.map +1 -1
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +56 -30
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +9 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +84 -8
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +5 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config.d.ts +7 -2
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +81 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/mock/connector-mock-server.js +2 -2
- package/dist/core/mock/connector-mock-server.js.map +1 -1
- package/dist/core/project/manifest.js +2 -2
- package/dist/core/project/manifest.js.map +1 -1
- package/dist/core/project/validation.d.ts.map +1 -1
- package/dist/core/project/validation.js +17 -0
- package/dist/core/project/validation.js.map +1 -1
- package/dist/core/scaffold/auth-generator.d.ts +28 -3
- package/dist/core/scaffold/auth-generator.d.ts.map +1 -1
- package/dist/core/scaffold/auth-generator.js +493 -23
- package/dist/core/scaffold/auth-generator.js.map +1 -1
- package/dist/core/scaffold/connector-functions-generator.d.ts +3 -3
- package/dist/core/scaffold/connector-functions-generator.d.ts.map +1 -1
- package/dist/core/scaffold/connector-functions-generator.js +23 -21
- package/dist/core/scaffold/connector-functions-generator.js.map +1 -1
- package/dist/core/scaffold/functions-generator.d.ts +2 -2
- package/dist/core/scaffold/functions-generator.d.ts.map +1 -1
- package/dist/core/scaffold/functions-generator.js +20 -18
- package/dist/core/scaffold/functions-generator.js.map +1 -1
- package/dist/core/scaffold/model-parser.d.ts.map +1 -1
- package/dist/core/scaffold/model-parser.js +8 -3
- package/dist/core/scaffold/model-parser.js.map +1 -1
- package/dist/core/scaffold/nextjs-generator.d.ts.map +1 -1
- package/dist/core/scaffold/nextjs-generator.js +13 -1
- package/dist/core/scaffold/nextjs-generator.js.map +1 -1
- package/dist/core/scaffold/ui-generator.d.ts +3 -0
- package/dist/core/scaffold/ui-generator.d.ts.map +1 -1
- package/dist/core/scaffold/ui-generator.js +12 -8
- package/dist/core/scaffold/ui-generator.js.map +1 -1
- package/dist/types/index.d.ts +35 -5
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/package-manager.d.ts.map +1 -1
- package/dist/utils/package-manager.js +1 -0
- package/dist/utils/package-manager.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/__snapshots__/functions-generator.test.ts.snap +20 -20
- package/src/__tests__/__snapshots__/nextjs-generator.test.ts.snap +13 -1
- package/src/__tests__/auth.test.ts +107 -0
- package/src/__tests__/config.test.ts +22 -1
- package/src/__tests__/dev.test.ts +27 -0
- package/src/__tests__/init.test.ts +19 -0
- package/src/__tests__/mcp.test.ts +2 -2
- package/src/__tests__/multi-auth.test.ts +133 -0
- package/src/__tests__/nextjs-generator.test.ts +8 -0
- package/src/cli/commands/add-auth.ts +351 -18
- package/src/cli/commands/dev.ts +116 -16
- package/src/cli/commands/init.ts +56 -30
- package/src/cli/commands/scaffold.ts +86 -10
- package/src/cli/index.ts +5 -1
- package/src/core/config.ts +69 -2
- package/src/core/mock/connector-mock-server.ts +2 -2
- package/src/core/project/manifest.ts +3 -3
- package/src/core/project/validation.ts +16 -0
- package/src/core/scaffold/auth-generator.ts +500 -24
- package/src/core/scaffold/connector-functions-generator.ts +26 -21
- package/src/core/scaffold/functions-generator.ts +21 -19
- package/src/core/scaffold/model-parser.ts +9 -3
- package/src/core/scaffold/nextjs-generator.ts +13 -1
- package/src/core/scaffold/ui-generator.ts +15 -8
- package/src/types/index.ts +36 -5
- package/src/utils/package-manager.ts +1 -0
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
ApiModelConnectorConfig,
|
|
12
12
|
ConnectorOperation,
|
|
13
13
|
ModelAuthPolicy,
|
|
14
|
+
AuthProvider,
|
|
14
15
|
} from "../../types";
|
|
15
16
|
import { generateAuthImportTS, generateAuthGuardTS } from "./auth-generator";
|
|
16
17
|
|
|
@@ -24,7 +25,8 @@ export function generateRdbConnectorFunctionTS(
|
|
|
24
25
|
sharedPackageName: string,
|
|
25
26
|
connectorDef: RdbConnectorConfig,
|
|
26
27
|
modelConnector: RdbModelConnectorConfig,
|
|
27
|
-
authPolicy?: ModelAuthPolicy
|
|
28
|
+
authPolicy?: ModelAuthPolicy,
|
|
29
|
+
authProvider?: AuthProvider
|
|
28
30
|
): string {
|
|
29
31
|
const modelCamel = toCamelCase(model.name);
|
|
30
32
|
const schemaName = model.schemaName;
|
|
@@ -122,8 +124,9 @@ export function generateRdbConnectorFunctionTS(
|
|
|
122
124
|
|
|
123
125
|
// Auth guard setup
|
|
124
126
|
const hasAuth = !!authPolicy;
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
+
const namedAuth = Boolean(authPolicy && (authPolicy.policy || typeof authPolicy.read === 'string' || typeof authPolicy.write === 'string'));
|
|
128
|
+
const authImport = hasAuth ? `\n${generateAuthImportTS(namedAuth)}\n` : '';
|
|
129
|
+
const readGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'read', authProvider)}\n` : '';
|
|
127
130
|
const authCatchBlock = hasAuth
|
|
128
131
|
? ` const authErr = handleAuthError(error);
|
|
129
132
|
if (authErr) return authErr;\n `
|
|
@@ -143,7 +146,7 @@ ${getConnection}
|
|
|
143
146
|
app.http('${modelCamel}-get-all', {
|
|
144
147
|
methods: ['GET'],
|
|
145
148
|
route: '${modelCamel}',
|
|
146
|
-
authLevel: '
|
|
149
|
+
authLevel: 'function',
|
|
147
150
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
148
151
|
try {${readGuard}
|
|
149
152
|
${queryAll}
|
|
@@ -162,7 +165,7 @@ app.http('${modelCamel}-get-all', {
|
|
|
162
165
|
app.http('${modelCamel}-get-by-id', {
|
|
163
166
|
methods: ['GET'],
|
|
164
167
|
route: '${modelCamel}/{id}',
|
|
165
|
-
authLevel: '
|
|
168
|
+
authLevel: 'function',
|
|
166
169
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
167
170
|
try {${readGuard}
|
|
168
171
|
const id = request.params.id;
|
|
@@ -190,7 +193,8 @@ export function generateApiConnectorFunctionTS(
|
|
|
190
193
|
sharedPackageName: string,
|
|
191
194
|
connectorDef: ApiConnectorConfig,
|
|
192
195
|
modelConnector: ApiModelConnectorConfig,
|
|
193
|
-
authPolicy?: ModelAuthPolicy
|
|
196
|
+
authPolicy?: ModelAuthPolicy,
|
|
197
|
+
authProvider?: AuthProvider
|
|
194
198
|
): string {
|
|
195
199
|
const modelCamel = toCamelCase(model.name);
|
|
196
200
|
const schemaName = model.schemaName;
|
|
@@ -202,9 +206,10 @@ export function generateApiConnectorFunctionTS(
|
|
|
202
206
|
|
|
203
207
|
// Auth guard setup (user access control, separate from connector auth to external APIs)
|
|
204
208
|
const hasAuth = !!authPolicy;
|
|
205
|
-
const
|
|
206
|
-
const
|
|
207
|
-
const
|
|
209
|
+
const namedAuth = Boolean(authPolicy && (authPolicy.policy || typeof authPolicy.read === 'string' || typeof authPolicy.write === 'string'));
|
|
210
|
+
const authImportLine = hasAuth ? `\n${generateAuthImportTS(namedAuth)}\n` : '';
|
|
211
|
+
const readGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'read', authProvider)}\n` : '';
|
|
212
|
+
const writeGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'write', authProvider)}\n` : '';
|
|
208
213
|
const authCatchBlock = hasAuth
|
|
209
214
|
? ` const authErr = handleAuthError(error);
|
|
210
215
|
if (authErr) return authErr;\n `
|
|
@@ -229,7 +234,7 @@ ${authSetup.helperCode}
|
|
|
229
234
|
app.http('${modelCamel}-get-all', {
|
|
230
235
|
methods: ['GET'],
|
|
231
236
|
route: '${modelCamel}',
|
|
232
|
-
authLevel: '
|
|
237
|
+
authLevel: 'function',
|
|
233
238
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
234
239
|
try {${readGuard}
|
|
235
240
|
const url = getBaseUrl() + '${epPath}';
|
|
@@ -263,7 +268,7 @@ app.http('${modelCamel}-get-all', {
|
|
|
263
268
|
app.http('${modelCamel}-get-by-id', {
|
|
264
269
|
methods: ['GET'],
|
|
265
270
|
route: '${modelCamel}/{id}',
|
|
266
|
-
authLevel: '
|
|
271
|
+
authLevel: 'function',
|
|
267
272
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
268
273
|
try {${readGuard}
|
|
269
274
|
const id = request.params.id;
|
|
@@ -303,7 +308,7 @@ app.http('${modelCamel}-get-by-id', {
|
|
|
303
308
|
app.http('${modelCamel}-create', {
|
|
304
309
|
methods: ['POST'],
|
|
305
310
|
route: '${modelCamel}',
|
|
306
|
-
authLevel: '
|
|
311
|
+
authLevel: 'function',
|
|
307
312
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
308
313
|
try {${writeGuard}
|
|
309
314
|
const body = await request.json();
|
|
@@ -341,7 +346,7 @@ app.http('${modelCamel}-create', {
|
|
|
341
346
|
app.http('${modelCamel}-update', {
|
|
342
347
|
methods: ['PUT'],
|
|
343
348
|
route: '${modelCamel}/{id}',
|
|
344
|
-
authLevel: '
|
|
349
|
+
authLevel: 'function',
|
|
345
350
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
346
351
|
try {${writeGuard}
|
|
347
352
|
const id = request.params.id;
|
|
@@ -381,7 +386,7 @@ app.http('${modelCamel}-update', {
|
|
|
381
386
|
app.http('${modelCamel}-delete', {
|
|
382
387
|
methods: ['DELETE'],
|
|
383
388
|
route: '${modelCamel}/{id}',
|
|
384
|
-
authLevel: '
|
|
389
|
+
authLevel: 'function',
|
|
385
390
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
386
391
|
try {${writeGuard}
|
|
387
392
|
const id = request.params.id;
|
|
@@ -496,7 +501,7 @@ export function generateRdbConnectorFunctionCSharp(
|
|
|
496
501
|
methods += `
|
|
497
502
|
[Function("${modelCamel}GetAll")]
|
|
498
503
|
public async Task<HttpResponseData> GetAll(
|
|
499
|
-
[HttpTrigger(AuthorizationLevel.
|
|
504
|
+
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}")] HttpRequestData request)
|
|
500
505
|
{
|
|
501
506
|
try
|
|
502
507
|
{
|
|
@@ -531,7 +536,7 @@ export function generateRdbConnectorFunctionCSharp(
|
|
|
531
536
|
methods += `
|
|
532
537
|
[Function("${modelCamel}GetById")]
|
|
533
538
|
public async Task<HttpResponseData> GetById(
|
|
534
|
-
[HttpTrigger(AuthorizationLevel.
|
|
539
|
+
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}/{id}")] HttpRequestData request,
|
|
535
540
|
string id)
|
|
536
541
|
{
|
|
537
542
|
try
|
|
@@ -618,7 +623,7 @@ export function generateApiConnectorFunctionCSharp(
|
|
|
618
623
|
methods += `
|
|
619
624
|
[Function("${modelCamel}GetAll")]
|
|
620
625
|
public async Task<HttpResponseData> GetAll(
|
|
621
|
-
[HttpTrigger(AuthorizationLevel.
|
|
626
|
+
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}")] HttpRequestData request)
|
|
622
627
|
{
|
|
623
628
|
try
|
|
624
629
|
{
|
|
@@ -651,7 +656,7 @@ export function generateApiConnectorFunctionCSharp(
|
|
|
651
656
|
methods += `
|
|
652
657
|
[Function("${modelCamel}GetById")]
|
|
653
658
|
public async Task<HttpResponseData> GetById(
|
|
654
|
-
[HttpTrigger(AuthorizationLevel.
|
|
659
|
+
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}/{id}")] HttpRequestData request,
|
|
655
660
|
string id)
|
|
656
661
|
{
|
|
657
662
|
try
|
|
@@ -689,7 +694,7 @@ export function generateApiConnectorFunctionCSharp(
|
|
|
689
694
|
methods += `
|
|
690
695
|
[Function("${modelCamel}Create")]
|
|
691
696
|
public async Task<HttpResponseData> Create(
|
|
692
|
-
[HttpTrigger(AuthorizationLevel.
|
|
697
|
+
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "${modelCamel}")] HttpRequestData request)
|
|
693
698
|
{
|
|
694
699
|
try
|
|
695
700
|
{
|
|
@@ -725,7 +730,7 @@ export function generateApiConnectorFunctionCSharp(
|
|
|
725
730
|
methods += `
|
|
726
731
|
[Function("${modelCamel}Update")]
|
|
727
732
|
public async Task<HttpResponseData> Update(
|
|
728
|
-
[HttpTrigger(AuthorizationLevel.
|
|
733
|
+
[HttpTrigger(AuthorizationLevel.Function, "put", Route = "${modelCamel}/{id}")] HttpRequestData request,
|
|
729
734
|
string id)
|
|
730
735
|
{
|
|
731
736
|
try
|
|
@@ -759,7 +764,7 @@ export function generateApiConnectorFunctionCSharp(
|
|
|
759
764
|
methods += `
|
|
760
765
|
[Function("${modelCamel}Delete")]
|
|
761
766
|
public async Task<HttpResponseData> Delete(
|
|
762
|
-
[HttpTrigger(AuthorizationLevel.
|
|
767
|
+
[HttpTrigger(AuthorizationLevel.Function, "delete", Route = "${modelCamel}/{id}")] HttpRequestData request,
|
|
763
768
|
string id)
|
|
764
769
|
{
|
|
765
770
|
try
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { ModelInfo, toCamelCase, toKebabCase } from "./model-parser";
|
|
8
|
-
import { ModelAuthPolicy } from "../../types";
|
|
8
|
+
import { AuthProvider, ModelAuthPolicy } from "../../types";
|
|
9
9
|
import { generateAuthImportTS, generateAuthGuardTS, generateAuthGuardCSharp, generateAuthGuardPython } from "./auth-generator";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Azure Functions エンティティファイルを生成(インラインハンドラー方式)
|
|
13
13
|
* 各ハンドラーがベタ書きされており、ビジネスロジックの追加・変更が容易
|
|
14
14
|
*/
|
|
15
|
-
export function generateCompactAzureFunctionsCRUD(model: ModelInfo, sharedPackageName: string, authPolicy?: ModelAuthPolicy): string {
|
|
15
|
+
export function generateCompactAzureFunctionsCRUD(model: ModelInfo, sharedPackageName: string, authPolicy?: ModelAuthPolicy, authProvider?: AuthProvider): string {
|
|
16
16
|
const modelName = model.name;
|
|
17
17
|
const modelCamel = toCamelCase(modelName);
|
|
18
18
|
const schemaName = model.schemaName;
|
|
@@ -22,9 +22,10 @@ export function generateCompactAzureFunctionsCRUD(model: ModelInfo, sharedPackag
|
|
|
22
22
|
const isIdPartition = partitionKeyField === 'id';
|
|
23
23
|
|
|
24
24
|
const hasAuth = !!authPolicy;
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
25
|
+
const namedAuth = Boolean(authPolicy && (authPolicy.policy || typeof authPolicy.read === 'string' || typeof authPolicy.write === 'string'));
|
|
26
|
+
const authImport = hasAuth ? `\n${generateAuthImportTS(namedAuth)}\n` : '';
|
|
27
|
+
const readGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'read', authProvider)}\n` : '';
|
|
28
|
+
const writeGuard = hasAuth ? `\n${generateAuthGuardTS(authPolicy!, 'write', authProvider)}\n` : '';
|
|
28
29
|
const authCatchBlock = hasAuth
|
|
29
30
|
? ` const authErr = handleAuthError(error);
|
|
30
31
|
if (authErr) return authErr;\n`
|
|
@@ -69,7 +70,7 @@ const containerName = '${modelName.endsWith('s') ? modelName : modelName + 's'}'
|
|
|
69
70
|
app.http('${modelCamel}-get-all', {
|
|
70
71
|
methods: ['GET'],
|
|
71
72
|
route: '${modelCamel}',
|
|
72
|
-
authLevel: '
|
|
73
|
+
authLevel: 'function',
|
|
73
74
|
extraInputs: [
|
|
74
75
|
{
|
|
75
76
|
type: 'cosmosDB',
|
|
@@ -105,7 +106,7 @@ ${getByIdHandler}
|
|
|
105
106
|
app.http('${modelCamel}-create', {
|
|
106
107
|
methods: ['POST'],
|
|
107
108
|
route: '${modelCamel}',
|
|
108
|
-
authLevel: '
|
|
109
|
+
authLevel: 'function',
|
|
109
110
|
extraOutputs: [
|
|
110
111
|
{
|
|
111
112
|
type: 'cosmosDB',
|
|
@@ -158,7 +159,7 @@ function generateGetByIdWithBinding(modelCamel: string, schemaName: string, read
|
|
|
158
159
|
app.http('${modelCamel}-get-by-id', {
|
|
159
160
|
methods: ['GET'],
|
|
160
161
|
route: '${modelCamel}/{id}',
|
|
161
|
-
authLevel: '
|
|
162
|
+
authLevel: 'function',
|
|
162
163
|
extraInputs: [
|
|
163
164
|
{
|
|
164
165
|
type: 'cosmosDB',
|
|
@@ -193,7 +194,7 @@ function generateGetByIdWithSdk(modelCamel: string, schemaName: string, readGuar
|
|
|
193
194
|
app.http('${modelCamel}-get-by-id', {
|
|
194
195
|
methods: ['GET'],
|
|
195
196
|
route: '${modelCamel}/{id}',
|
|
196
|
-
authLevel: '
|
|
197
|
+
authLevel: 'function',
|
|
197
198
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
198
199
|
try {${readGuard}
|
|
199
200
|
const id = request.params.id;
|
|
@@ -229,7 +230,7 @@ function generateUpdateWithBinding(modelCamel: string, schemaName: string, write
|
|
|
229
230
|
app.http('${modelCamel}-update', {
|
|
230
231
|
methods: ['PUT'],
|
|
231
232
|
route: '${modelCamel}/{id}',
|
|
232
|
-
authLevel: '
|
|
233
|
+
authLevel: 'function',
|
|
233
234
|
extraInputs: [
|
|
234
235
|
{
|
|
235
236
|
type: 'cosmosDB',
|
|
@@ -294,7 +295,7 @@ function generateUpdateWithSdk(modelCamel: string, schemaName: string, partition
|
|
|
294
295
|
app.http('${modelCamel}-update', {
|
|
295
296
|
methods: ['PUT'],
|
|
296
297
|
route: '${modelCamel}/{id}',
|
|
297
|
-
authLevel: '
|
|
298
|
+
authLevel: 'function',
|
|
298
299
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
299
300
|
try {${writeGuard}
|
|
300
301
|
const id = request.params.id;
|
|
@@ -349,7 +350,7 @@ function generateDeleteIdPartition(modelCamel: string, writeGuard: string, authC
|
|
|
349
350
|
app.http('${modelCamel}-delete', {
|
|
350
351
|
methods: ['DELETE'],
|
|
351
352
|
route: '${modelCamel}/{id}',
|
|
352
|
-
authLevel: '
|
|
353
|
+
authLevel: 'function',
|
|
353
354
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
354
355
|
try {${writeGuard}
|
|
355
356
|
const id = request.params.id;
|
|
@@ -379,7 +380,7 @@ function generateDeleteCustomPartition(modelCamel: string, partitionKeyField: st
|
|
|
379
380
|
app.http('${modelCamel}-delete', {
|
|
380
381
|
methods: ['DELETE'],
|
|
381
382
|
route: '${modelCamel}/{id}',
|
|
382
|
-
authLevel: '
|
|
383
|
+
authLevel: 'function',
|
|
383
384
|
handler: async (request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> => {
|
|
384
385
|
try {${writeGuard}
|
|
385
386
|
const id = request.params.id;
|
|
@@ -596,7 +597,7 @@ ${readItemMethod}
|
|
|
596
597
|
|
|
597
598
|
[Function("${modelCamel}GetAll")]
|
|
598
599
|
public async Task<HttpResponseData> GetAll(
|
|
599
|
-
[HttpTrigger(AuthorizationLevel.
|
|
600
|
+
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}")] HttpRequestData request)
|
|
600
601
|
{
|
|
601
602
|
try
|
|
602
603
|
{${readGuard}
|
|
@@ -637,7 +638,7 @@ ${readItemMethod}
|
|
|
637
638
|
|
|
638
639
|
[Function("${modelCamel}GetById")]
|
|
639
640
|
public async Task<HttpResponseData> GetById(
|
|
640
|
-
[HttpTrigger(AuthorizationLevel.
|
|
641
|
+
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "${modelCamel}/{id}")] HttpRequestData request,
|
|
641
642
|
string id)
|
|
642
643
|
{
|
|
643
644
|
try
|
|
@@ -660,7 +661,7 @@ ${readItemMethod}
|
|
|
660
661
|
|
|
661
662
|
[Function("${modelCamel}Create")]
|
|
662
663
|
public async Task<HttpResponseData> Create(
|
|
663
|
-
[HttpTrigger(AuthorizationLevel.
|
|
664
|
+
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "${modelCamel}")] HttpRequestData request)
|
|
664
665
|
{
|
|
665
666
|
try
|
|
666
667
|
{${writeGuard}
|
|
@@ -695,7 +696,7 @@ ${readItemMethod}
|
|
|
695
696
|
|
|
696
697
|
[Function("${modelCamel}Update")]
|
|
697
698
|
public async Task<HttpResponseData> Update(
|
|
698
|
-
[HttpTrigger(AuthorizationLevel.
|
|
699
|
+
[HttpTrigger(AuthorizationLevel.Function, "put", Route = "${modelCamel}/{id}")] HttpRequestData request,
|
|
699
700
|
string id)
|
|
700
701
|
{
|
|
701
702
|
try
|
|
@@ -740,7 +741,7 @@ ${readItemMethod}
|
|
|
740
741
|
|
|
741
742
|
[Function("${modelCamel}Delete")]
|
|
742
743
|
public async Task<HttpResponseData> Delete(
|
|
743
|
-
[HttpTrigger(AuthorizationLevel.
|
|
744
|
+
[HttpTrigger(AuthorizationLevel.Function, "delete", Route = "${modelCamel}/{id}")] HttpRequestData request,
|
|
744
745
|
string id)
|
|
745
746
|
{
|
|
746
747
|
try
|
|
@@ -779,7 +780,8 @@ export function generatePythonAzureFunctionsCRUD(model: ModelInfo, authPolicy?:
|
|
|
779
780
|
const isIdPartition = partitionKeyField === 'id';
|
|
780
781
|
|
|
781
782
|
const hasAuth = !!authPolicy;
|
|
782
|
-
const
|
|
783
|
+
const namedAuth = Boolean(authPolicy && (authPolicy.policy || typeof authPolicy.read === 'string' || typeof authPolicy.write === 'string'));
|
|
784
|
+
const authImport = hasAuth ? `\nfrom auth.${namedAuth ? 'auth_router' : 'jwt_helper'} import require_auth, require_roles, handle_auth_error\n` : '';
|
|
783
785
|
// generateAuthGuardPython outputs at 4-space indent; inside try: we need 8-space
|
|
784
786
|
const readGuardRaw = hasAuth ? generateAuthGuardPython(authPolicy!, 'read') : '';
|
|
785
787
|
const writeGuardRaw = hasAuth ? generateAuthGuardPython(authPolicy!, 'write') : '';
|
|
@@ -779,14 +779,20 @@ export function parseAuthPolicy(content: string): ModelAuthPolicy | undefined {
|
|
|
779
779
|
const read = extractRoles('read');
|
|
780
780
|
const write = extractRoles('write');
|
|
781
781
|
|
|
782
|
-
|
|
782
|
+
const extractName = (key: string): string | undefined => objectStr.match(new RegExp(`${key}\\s*:\\s*['"]([^'"]+)['"]`))?.[1];
|
|
783
|
+
const namedPolicy = extractName('policy');
|
|
784
|
+
const namedRead = extractName('read');
|
|
785
|
+
const namedWrite = extractName('write');
|
|
786
|
+
|
|
787
|
+
if (!roles && !read && !write && !namedPolicy && !namedRead && !namedWrite) {
|
|
783
788
|
return undefined;
|
|
784
789
|
}
|
|
785
790
|
|
|
786
791
|
return {
|
|
787
792
|
...(roles ? { roles } : {}),
|
|
788
|
-
...(read ? { read } : {}),
|
|
789
|
-
...(write ? { write } : {}),
|
|
793
|
+
...(namedRead ? { read: namedRead } : read ? { read } : {}),
|
|
794
|
+
...(namedWrite ? { write: namedWrite } : write ? { write } : {}),
|
|
795
|
+
...(namedPolicy ? { policy: namedPolicy } : {}),
|
|
790
796
|
};
|
|
791
797
|
}
|
|
792
798
|
|
|
@@ -32,6 +32,17 @@ function getFunctionsBaseUrl(): string {
|
|
|
32
32
|
return process.env.BACKEND_FUNCTIONS_BASE_URL || process.env.FUNCTIONS_BASE_URL || 'http://localhost:7071';
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
function isLocalFunctionsUrl(url: string): boolean {
|
|
36
|
+
return url.startsWith('http://localhost:') || url.startsWith('http://127.0.0.1:');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getFunctionsKeyHeaders(functionsBaseUrl: string): Record<string, string> {
|
|
40
|
+
const key = process.env.BACKEND_FUNCTIONS_KEY;
|
|
41
|
+
if (key) return { 'x-functions-key': key };
|
|
42
|
+
if (isLocalFunctionsUrl(functionsBaseUrl)) return {};
|
|
43
|
+
throw new Error('BACKEND_FUNCTIONS_KEY is required for non-local Functions calls');
|
|
44
|
+
}
|
|
45
|
+
|
|
35
46
|
interface CallFunctionConfig<TInput = any, TOutput = any> {
|
|
36
47
|
/** HTTP メソッド */
|
|
37
48
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
@@ -70,11 +81,12 @@ export async function callFunction<TInput = any, TOutput = any>(
|
|
|
70
81
|
// Azure Functions を呼び出し
|
|
71
82
|
const functionsBaseUrl = getFunctionsBaseUrl();
|
|
72
83
|
const url = functionsBaseUrl + path;
|
|
84
|
+
const keyHeaders = getFunctionsKeyHeaders(functionsBaseUrl);
|
|
73
85
|
console.log(\`[BFF] \${method} \${url}\`);
|
|
74
86
|
|
|
75
87
|
const response = await fetch(url, {
|
|
76
88
|
method,
|
|
77
|
-
headers: { 'Content-Type': 'application/json' },
|
|
89
|
+
headers: { 'Content-Type': 'application/json', ...keyHeaders },
|
|
78
90
|
body: validatedBody !== undefined ? JSON.stringify(validatedBody) : undefined,
|
|
79
91
|
});
|
|
80
92
|
|
|
@@ -10,6 +10,9 @@ import { ModelAuthPolicy } from "../../types";
|
|
|
10
10
|
export interface UIAuthOptions {
|
|
11
11
|
/** モデル固有のロールポリシー */
|
|
12
12
|
authPolicy: ModelAuthPolicy;
|
|
13
|
+
writeRoles?: string[];
|
|
14
|
+
authenticatedFetchImport?: string;
|
|
15
|
+
authContextImport?: string;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
/**
|
|
@@ -21,7 +24,8 @@ export function generateListPage(model: ModelInfo, sharedPackageName: string, au
|
|
|
21
24
|
const modelKebab = toKebabCase(modelName);
|
|
22
25
|
|
|
23
26
|
const hasAuth = !!authOptions;
|
|
24
|
-
const
|
|
27
|
+
const authContextImport = authOptions?.authContextImport ?? '@/lib/auth/auth-context';
|
|
28
|
+
const writeRoles = authOptions?.writeRoles ?? (Array.isArray(authOptions?.authPolicy?.write) ? authOptions.authPolicy.write : authOptions?.authPolicy?.roles);
|
|
25
29
|
const hasWriteRoles = hasAuth && writeRoles && writeRoles.length > 0;
|
|
26
30
|
|
|
27
31
|
// フィールドから表示するカラムを抽出(id以外の最初の3つ)
|
|
@@ -71,7 +75,7 @@ import { useEffect, useState } from 'react';
|
|
|
71
75
|
import Link from 'next/link';
|
|
72
76
|
import { z } from 'zod/v4';
|
|
73
77
|
${schemaImportLine}
|
|
74
|
-
${hasAuth ? `import { useAuth } from '
|
|
78
|
+
${hasAuth ? `import { useAuth } from '${authContextImport}';` : ''}
|
|
75
79
|
|
|
76
80
|
type ${modelName} = z.infer<typeof ${localSchemaName}>;
|
|
77
81
|
|
|
@@ -266,7 +270,8 @@ export function generateDetailPage(model: ModelInfo, sharedPackageName: string,
|
|
|
266
270
|
const modelKebab = toKebabCase(modelName);
|
|
267
271
|
|
|
268
272
|
const hasAuth = !!authOptions;
|
|
269
|
-
const
|
|
273
|
+
const authContextImport = authOptions?.authContextImport ?? '@/lib/auth/auth-context';
|
|
274
|
+
const writeRoles = authOptions?.writeRoles ?? (Array.isArray(authOptions?.authPolicy?.write) ? authOptions.authPolicy.write : authOptions?.authPolicy?.roles);
|
|
270
275
|
const hasWriteRoles = hasAuth && writeRoles && writeRoles.length > 0;
|
|
271
276
|
|
|
272
277
|
// 外部キーフィールドを検出
|
|
@@ -311,7 +316,7 @@ import { useParams, useRouter } from 'next/navigation';
|
|
|
311
316
|
import Link from 'next/link';
|
|
312
317
|
import { z } from 'zod/v4';
|
|
313
318
|
${schemaImportLine}
|
|
314
|
-
${hasAuth ? `import { useAuth } from '
|
|
319
|
+
${hasAuth ? `import { useAuth } from '${authContextImport}';` : ''}
|
|
315
320
|
|
|
316
321
|
type ${modelName} = z.infer<typeof ${localSchemaName}>;
|
|
317
322
|
|
|
@@ -910,7 +915,8 @@ export function generateNewPage(model: ModelInfo, authOptions?: UIAuthOptions):
|
|
|
910
915
|
const modelKebab = toKebabCase(modelName);
|
|
911
916
|
|
|
912
917
|
const hasAuth = !!authOptions;
|
|
913
|
-
const
|
|
918
|
+
const authContextImport = authOptions?.authContextImport ?? '@/lib/auth/auth-context';
|
|
919
|
+
const writeRoles = authOptions?.writeRoles ?? (Array.isArray(authOptions?.authPolicy?.write) ? authOptions.authPolicy.write : authOptions?.authPolicy?.roles);
|
|
914
920
|
const hasWriteRoles = hasAuth && writeRoles && writeRoles.length > 0;
|
|
915
921
|
|
|
916
922
|
if (!hasAuth) {
|
|
@@ -936,7 +942,7 @@ export default function New${modelName}Page() {
|
|
|
936
942
|
import { useEffect } from 'react';
|
|
937
943
|
import { useRouter } from 'next/navigation';
|
|
938
944
|
import ${modelName}Form from '../_components/${modelName}Form';
|
|
939
|
-
import { useAuth } from '
|
|
945
|
+
import { useAuth } from '${authContextImport}';
|
|
940
946
|
|
|
941
947
|
export default function New${modelName}Page() {
|
|
942
948
|
const router = useRouter();
|
|
@@ -979,7 +985,8 @@ export function generateEditPage(model: ModelInfo, sharedPackageName: string, au
|
|
|
979
985
|
const modelKebab = toKebabCase(modelName);
|
|
980
986
|
|
|
981
987
|
const hasAuth = !!authOptions;
|
|
982
|
-
const
|
|
988
|
+
const authContextImport = authOptions?.authContextImport ?? '@/lib/auth/auth-context';
|
|
989
|
+
const writeRoles = authOptions?.writeRoles ?? (Array.isArray(authOptions?.authPolicy?.write) ? authOptions.authPolicy.write : authOptions?.authPolicy?.roles);
|
|
983
990
|
const hasWriteRoles = hasAuth && writeRoles && writeRoles.length > 0;
|
|
984
991
|
|
|
985
992
|
const schemaName = model.schemaName;
|
|
@@ -997,7 +1004,7 @@ import { useParams, useRouter } from 'next/navigation';
|
|
|
997
1004
|
import ${modelName}Form from '../../_components/${modelName}Form';
|
|
998
1005
|
import { z } from 'zod/v4';
|
|
999
1006
|
${schemaImportLine}
|
|
1000
|
-
${hasAuth ? `import { useAuth } from '
|
|
1007
|
+
${hasAuth ? `import { useAuth } from '${authContextImport}';` : ''}
|
|
1001
1008
|
|
|
1002
1009
|
type ${modelName} = z.infer<typeof ${localSchemaName}>;
|
|
1003
1010
|
|
package/src/types/index.ts
CHANGED
|
@@ -73,7 +73,7 @@ export interface ApiModelConnectorConfig {
|
|
|
73
73
|
export type ModelConnectorConfig = RdbModelConnectorConfig | ApiModelConnectorConfig;
|
|
74
74
|
|
|
75
75
|
// 認証プロバイダー種別
|
|
76
|
-
export type AuthProvider = "custom-jwt" | "swa" | "swa-custom" | "none";
|
|
76
|
+
export type AuthProvider = "custom-jwt" | "swa" | "external-token" | "swa-custom" | "none";
|
|
77
77
|
|
|
78
78
|
// custom-jwt プロバイダー設定
|
|
79
79
|
export interface CustomJwtConfig {
|
|
@@ -95,12 +95,25 @@ export interface SwaAuthConfig {
|
|
|
95
95
|
|
|
96
96
|
// 認可設定
|
|
97
97
|
export interface AuthorizationConfig {
|
|
98
|
-
defaultPolicy?: "authenticated" | "anonymous";
|
|
98
|
+
defaultPolicy?: "authenticated" | "anonymous" | "public" | string;
|
|
99
|
+
policies?: Record<string, AuthorizationPolicy>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface AuthorizationPolicy {
|
|
103
|
+
schemes: string[];
|
|
104
|
+
roles?: string[];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface AuthSchemeConfig {
|
|
108
|
+
provider: AuthProvider;
|
|
109
|
+
customJwt?: CustomJwtConfig;
|
|
110
|
+
swa?: SwaAuthConfig;
|
|
99
111
|
}
|
|
100
112
|
|
|
101
113
|
// 認証設定
|
|
102
114
|
export interface AuthConfig {
|
|
103
|
-
provider
|
|
115
|
+
provider?: AuthProvider;
|
|
116
|
+
schemes?: Record<string, AuthSchemeConfig>;
|
|
104
117
|
customJwt?: CustomJwtConfig;
|
|
105
118
|
swa?: SwaAuthConfig;
|
|
106
119
|
authorization?: AuthorizationConfig;
|
|
@@ -109,8 +122,26 @@ export interface AuthConfig {
|
|
|
109
122
|
// モデルに付与する認可ポリシー
|
|
110
123
|
export interface ModelAuthPolicy {
|
|
111
124
|
roles?: string[];
|
|
112
|
-
read?: string[];
|
|
113
|
-
write?: string[];
|
|
125
|
+
read?: string[] | string;
|
|
126
|
+
write?: string[] | string;
|
|
127
|
+
policy?: string;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface NormalizedAuthConfig extends Omit<AuthConfig, "schemes" | "authorization"> {
|
|
131
|
+
schemes: Record<string, AuthSchemeConfig>;
|
|
132
|
+
authorization: AuthorizationConfig & { defaultPolicy: string; policies: Record<string, AuthorizationPolicy> };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface AuthPrincipal {
|
|
136
|
+
subject: string;
|
|
137
|
+
scheme: string;
|
|
138
|
+
issuer: string;
|
|
139
|
+
roles: string[];
|
|
140
|
+
claims: Record<string, unknown>;
|
|
141
|
+
/** @deprecated Use subject. */
|
|
142
|
+
userId?: string;
|
|
143
|
+
/** @deprecated Read provider data from claims. */
|
|
144
|
+
userDetails?: string;
|
|
114
145
|
}
|
|
115
146
|
|
|
116
147
|
// CLI設定の型
|
|
@@ -178,6 +178,7 @@ export function getCiSetupStep(pm: PackageManager): string {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
export function getCiInstallCommand(ciPm: PackageManager, projectPm: PackageManager = ciPm): string {
|
|
181
|
+
void projectPm; // Reserved for package-manager-specific CI bridging.
|
|
181
182
|
if (ciPm === "pnpm") {
|
|
182
183
|
return getCommands(ciPm).ci;
|
|
183
184
|
}
|