diff --git a/.claude/commands/prepare-release.md b/.claude/commands/prepare-release.md index 873a211..4a6d204 100644 --- a/.claude/commands/prepare-release.md +++ b/.claude/commands/prepare-release.md @@ -25,9 +25,9 @@ Run: ``` git log --oneline ..HEAD ``` -or if tags are not present, compare with `main`: +or if tags are not present, compare with `origin/main`: ``` -git log --oneline main..HEAD +git log --oneline origin/main..HEAD ``` Read the commit messages to understand what changed. Ignore pure chore/refactor commits (e.g. CLAUDE.md updates, code generation runs) unless they are meaningful to library consumers. @@ -61,7 +61,9 @@ Rules: Stage only `pubspec.yaml` and `CHANGELOG.md`, then commit: ``` git add pubspec.yaml CHANGELOG.md -git commit -m "chore: bump version to and update changelog" +git commit -m "chore: bump version to and update changelog + +Co-Authored-By: Claude Sonnet 4.6 " ``` ### 6. Push and open PR diff --git a/CHANGELOG.md b/CHANGELOG.md index 332a954..38c17f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 3.6.24 + +- Added `graphqlIdFragment` and `graphqlUuidFragment` static constants to `Access` for GraphQL queries. +- Added `save()` and `delete()` methods to `AccessInput` with full GraphQL mutation support (both ID and UUID variants). +- Updated `Poi.graphqlFragment` to include the `access` field using the UUID access fragment. + ## 3.6.23 - CI issues, no code changes. diff --git a/CLAUDE.md b/CLAUDE.md index 4219f2d..d96a5d1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -151,3 +151,4 @@ Each test file typically tests: - "wrap it up" - Means review the changes, commit and push, and create a pull request if the user wants - Do not include Test plan on the PR body under any circumstances, the code should be tested before the PR. +- Always include `Co-Authored-By: Claude Sonnet 4.6 ` as a footer in every git commit message. diff --git a/lib/src/access/access.dart b/lib/src/access/access.dart index c618afa..67567b2 100644 --- a/lib/src/access/access.dart +++ b/lib/src/access/access.dart @@ -2,6 +2,7 @@ library; import 'package:collection/collection.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:layrz_logging/layrz_logging.dart'; import 'package:layrz_models/layrz_models.dart'; part 'access.freezed.dart'; diff --git a/lib/src/access/access.freezed.dart b/lib/src/access/access.freezed.dart index 98debca..92c88d7 100644 --- a/lib/src/access/access.freezed.dart +++ b/lib/src/access/access.freezed.dart @@ -228,8 +228,8 @@ return $default(_that.id,_that.label,_that.read,_that.write,_that.manage,_that.o /// @nodoc @JsonSerializable() -class _Access implements Access { - const _Access({required this.id, this.label, this.read, this.write, this.manage, this.objectId, required this.userId, this.user, @JsonKey(unknownEnumValue: AccessModule.unknown) required this.module}); +class _Access extends Access { + const _Access({required this.id, this.label, this.read, this.write, this.manage, this.objectId, required this.userId, this.user, @JsonKey(unknownEnumValue: AccessModule.unknown) required this.module}): super._(); factory _Access.fromJson(Map json) => _$AccessFromJson(json); @override final String id; @@ -532,8 +532,8 @@ return $default(_that.id,_that.read,_that.write,_that.manage,_that.objectId,_tha /// @nodoc @JsonSerializable() -class _AccessInput implements AccessInput { - _AccessInput({this.id, this.read = false, this.write = false, this.manage = false, this.objectId, this.userId, @JsonKey(unknownEnumValue: AccessModule.unknown) required this.module}); +class _AccessInput extends AccessInput { + _AccessInput({this.id, this.read = false, this.write = false, this.manage = false, this.objectId, this.userId, @JsonKey(unknownEnumValue: AccessModule.unknown) required this.module}): super._(); factory _AccessInput.fromJson(Map json) => _$AccessInputFromJson(json); /// Represents the id of the access. diff --git a/lib/src/access/src/access.dart b/lib/src/access/src/access.dart index 1e25882..5d0d711 100644 --- a/lib/src/access/src/access.dart +++ b/lib/src/access/src/access.dart @@ -1,10 +1,11 @@ part of '../access.dart'; @freezed - /// AccessPermission /// Access Permission class abstract class Access with _$Access { + const Access._(); + const factory Access({ required String id, String? label, @@ -18,4 +19,30 @@ abstract class Access with _$Access { }) = _Access; factory Access.fromJson(Map json) => _$AccessFromJson(json); + + /// [graphqlIdFragment] GraphQL fragment for Access + static const String graphqlIdFragment = ''' + fragment accessFragment on AccessPermission { + id + read + write + manage + objectId + userId + module + } + '''; + + /// [graphqlUuidFragment] GraphQL fragment for Access using UUID + static const String graphqlUuidFragment = ''' + fragment accessUuidFragment on AccessPermissionUuid { + id + read + write + manage + objectId + userId + module + } + '''; } diff --git a/lib/src/access/src/access_input.dart b/lib/src/access/src/access_input.dart index 21dc30c..6859e75 100644 --- a/lib/src/access/src/access_input.dart +++ b/lib/src/access/src/access_input.dart @@ -2,6 +2,7 @@ part of '../access.dart'; @unfreezed abstract class AccessInput with _$AccessInput { + const AccessInput._(); factory AccessInput({ /// Represents the id of the access. String? id, @@ -26,4 +27,166 @@ abstract class AccessInput with _$AccessInput { }) = _AccessInput; factory AccessInput.fromJson(Map json) => _$AccessInputFromJson(json); + + /// [save] saves the access input to the server + /// It returns a [bool] indicating whether the save was successful + Future save({ + /// [apiToken] is the API token to use for authentication. You can get one using the `login` mutation + /// on the GraphQL API. + required String apiToken, + + /// [uri] is the GraphQL endpoint to use + required Uri uri, + + /// [onResponse] is the callback to call when the response is received + void Function(String statusCode)? onResponse, + + /// [useUuid] whether to use UUID for deletion + bool useUuid = false, + }) async { + final connector = LayrzConnector(uri: uri); + try { + final response = await connector.perform( + query: id == null + ? (useUuid ? AccessInput.addUuidGraphqlMutation : AccessInput.addIdGraphqlMutation) + : (useUuid ? AccessInput.editUuidGraphqlMutation : AccessInput.editIdGraphqlMutation), + variables: {'apiToken': apiToken, 'data': toJson()}, + ); + + final data = response.data; + if (data == null) { + onResponse?.call('INTERNAL_ERROR'); + Log.error("layrz_models/AccessInput/save(): No response from server"); + return false; + } + + final result = id == null + ? (useUuid ? data['data']['addAccessPermissionUuid'] : data['data']['addAccessPermission']) + : (useUuid ? data['data']['editAccessPermissionUuid'] : data['data']['editAccessPermission']); + if (result == null) { + onResponse?.call('INTERNAL_ERROR'); + Log.error("layrz_models/AccessInput/save(): No result from server"); + return false; + } + + if (result['status'] != 'OK') { + onResponse?.call(result['status']); + return false; + } + + return true; + } catch (e, stack) { + Log.critical("layrz_models/AccessInput/save(): General exception => $e\n$stack"); + return false; + } + } + + /// [delete] delete AccessPermission from the server + Future delete({ + /// [apiToken] is the API token to use for authentication. You can get one using the `login` mutation + /// on the GraphQL API. + required String apiToken, + + /// [uri] is the GraphQL endpoint to use + required Uri uri, + + /// [onResponse] is the callback to call when the response is received + void Function(String statusCode)? onResponse, + + /// [useUuid] whether to use UUID for deletion + bool useUuid = false, + }) async { + final connector = LayrzConnector(uri: uri); + + try { + final response = await connector.perform( + query: useUuid ? AccessInput.deleteUuidGraphqlMutation : AccessInput.deleteIdGraphqlMutation, + variables: {'apiToken': apiToken, 'data': toJson()}, + ); + + final data = response.data; + if (data == null) { + onResponse?.call('INTERNAL_ERROR'); + Log.error("layrz_models/Access/delete(): No response from server"); + return false; + } + + final result = data['data'][useUuid ? 'deleteAccessPermissionUuid' : 'deleteAccessPermission']; + if (result == null) { + onResponse?.call('INTERNAL_ERROR'); + Log.error("layrz_models/Access/delete(): No result from server"); + return false; + } + + if (result['status'] != 'OK') { + onResponse?.call(result['status']); + return false; + } + + return true; + } catch (e, stack) { + Log.critical("layrz_models/Access/delete(): General exception => $e\n$stack"); + return false; + } + } + + /// [addIdGraphqlMutation] GraphQL mutation for adding an access permission + static String get addIdGraphqlMutation => r''' + mutation($apiToken: String!, $data: AccessPermissionInput!) { + addAccessPermission(apiToken: $apiToken, data: $data) { + status + errors + } + } + '''; + + /// [addUuidGraphqlMutation] GraphQL mutation for adding an access permission + static String get addUuidGraphqlMutation => r''' + mutation($apiToken: String!, $data: AccessPermissionUuidInput!) { + addAccessPermissionUuid(apiToken: $apiToken, data: $data) { + status + errors + } + } + '''; + + /// [editIdGraphqlMutation] GraphQL mutation for updating an access permission + static String get editIdGraphqlMutation => r''' + mutation($apiToken: String!, $data: AccessPermissionInput!) { + editAccessPermission(apiToken: $apiToken, data: $data) { + status + errors + } + } + '''; + + /// [editUuidGraphqlMutation] GraphQL mutation for updating an access permission + static String get editUuidGraphqlMutation => r''' + mutation($apiToken: String!, $data: AccessPermissionUuidInput!) { + editAccessPermissionUuid(apiToken: $apiToken, data: $data) { + status + errors + } + } + '''; + + /// [deleteIdGraphqlMutation] GraphQL mutation for deleting an access permission + static String get deleteIdGraphqlMutation => r''' + mutation($apiToken: String!, $data: AccessPermissionInput!) { + deleteAccessPermission(apiToken: $apiToken, data: $data) { + status + errors + } + } + '''; + + /// [deleteUuidGraphqlMutation] GraphQL mutation for deleting an access permission + static String get deleteUuidGraphqlMutation => r''' + mutation($apiToken: String!, $data: AccessPermissionUuidInput!) { + deleteAccessPermissionUuid(apiToken: $apiToken, data: $data) { + status + errors + } + } + '''; } diff --git a/lib/src/map/src/poi.dart b/lib/src/map/src/poi.dart index 51f61db..571961a 100644 --- a/lib/src/map/src/poi.dart +++ b/lib/src/map/src/poi.dart @@ -208,7 +208,9 @@ abstract class Poi with _$Poi { /// [graphqlFragment] is the GraphQL fragment to fetch the POI data /// It includes the basic user fields fragment [basicUserFields] to get the user data - static String get graphqlFragment => ''' + static String get graphqlFragment => + ''' + ${Access.graphqlUuidFragment} fragment poiFragment on Poi { id name @@ -216,6 +218,10 @@ abstract class Poi with _$Poi { icon latitude longitude + + access { + ...accessUuidFragment + } } '''; diff --git a/pubspec.yaml b/pubspec.yaml index d207840..b84dbd4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ description: Layrz API models for Dart/Flutter. This package contains the models used by the Layrz API. name: layrz_models -version: "3.6.23" +version: "3.6.24" repository: https://github.com/goldenm-software/layrz_models environment: