Rename, GraphQL Flutter Cookie Basics, & GraphQL Flutter Model Gen

This commit is contained in:
Evan Reichard 2021-02-10 00:09:57 -05:00
parent a2d0432147
commit f0aee561ad
41 changed files with 1461 additions and 258 deletions

View File

@ -1,18 +1,15 @@
#uImagini
## Running Server
# cd ./cmd/
CONFIG_PATH=$(pwd) DATA_PATH=$(pwd) go run main.go serve
## Generate GraphQL Models
# cd ./cmd/
go run github.com/99designs/gqlgen generate
go run main.go generate
## Generate GraphQL Documentation
# From app root
graphdoc -e http://localhost:8484/query -o ./docs/schema
@ -20,3 +17,6 @@
## Flutter Build
flutter run
## Generate GraphQL Flutter Models
flutter pub run build_runner build

View File

@ -204,12 +204,16 @@ func (r *queryResolver) Login(ctx context.Context, user string, password string,
return &model.AuthResponse{Result: model.AuthResultFailure}, nil
}
// Set appropriate cookies
accessCookie = http.Cookie{Name: "AccessToken", Value: accessToken, Path: "/", HttpOnly: true}
refreshCookie = http.Cookie{Name: "RefreshToken", Value: refreshToken, Path: "/", HttpOnly: true}
// Set appropriate cookies (TODO: Only for web!)
accessCookie = http.Cookie{Name: "AccessToken", Value: accessToken, Path: "/", HttpOnly: false}
refreshCookie = http.Cookie{Name: "RefreshToken", Value: refreshToken, Path: "/", HttpOnly: false}
http.SetCookie(*resp, &accessCookie)
http.SetCookie(*resp, &refreshCookie)
// Only for iOS & Android (TODO: Remove for web! Only cause affected by CORS during development)
(*resp).Header().Set("X-Imagini-AccessToken", accessToken)
(*resp).Header().Set("X-Imagini-RefreshToken", refreshToken)
return &model.AuthResponse{Result: model.AuthResultSuccess, Device: &foundDevice}, nil
}

View File

@ -50,6 +50,9 @@ func (api *API) refreshTokens(refreshToken jwt.Token) (string, string, error) {
}
func (api *API) validateTokens(w *http.ResponseWriter, r *http.Request) (jwt.Token, error) {
// TODO: Check from X-Imagini-AccessToken
// TODO: Check from X-Imagini-RefreshToken
// Validate Access Token
accessCookie, _ := r.Cookie("AccessToken")
if accessCookie != nil {
@ -72,23 +75,29 @@ func (api *API) validateTokens(w *http.ResponseWriter, r *http.Request) (jwt.Tok
}
// Refresh Access Token & Generate New Refresh Token
newAccessCookie, newRefreshCookie, err := api.refreshTokens(refreshToken)
newAccessToken, newRefreshToken, err := api.refreshTokens(refreshToken)
if err != nil {
return nil, err
}
// TODO: Actually Refresh Refresh Token
newRefreshCookie = refreshCookie.Value
newRefreshToken = refreshCookie.Value
// Set appropriate cookies (TODO: Only for web!)
// Update Access & Refresh Cookies
http.SetCookie(*w, &http.Cookie{
Name: "AccessToken",
Value: newAccessCookie,
Value: newAccessToken,
})
http.SetCookie(*w, &http.Cookie{
Name: "RefreshToken",
Value: newRefreshCookie,
Value: newRefreshToken,
})
return jwt.ParseBytes([]byte(newAccessCookie))
// Only for iOS & Android (TODO: Remove for web! Only cause affected by CORS during development)
(*w).Header().Set("X-Imagini-AccessToken", newAccessToken)
(*w).Header().Set("X-Imagini-RefreshToken", newRefreshToken)
return jwt.ParseBytes([]byte(newAccessToken))
}

View File

@ -24,8 +24,16 @@ func multipleMiddleware(h http.HandlerFunc, m ...Middleware) http.HandlerFunc {
* This is used for the graphQL endpoints that may require access to the
* Request and ResponseWriter variables. These are used to get / set cookies.
**/
func (api *API) contextMiddleware(next http.Handler) http.Handler {
func (api *API) queryMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// TODO: REMOVE (SOME OF) THIS!! Only for developement due to CORS
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Expose-Headers", "*")
w.Header().Set("Access-Control-Allow-Headers", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
authContext := &model.AuthContext{
AuthResponse: &w,
AuthRequest: r,

View File

@ -26,7 +26,7 @@ func (api *API) registerRoutes() {
// Handle GraphQL
api.Router.Handle("/playground", playground.Handler("GraphQL playground", "/query"))
api.Router.Handle("/query", api.contextMiddleware(srv))
api.Router.Handle("/query", api.queryMiddleware(srv))
// Handle Resource Route
api.Router.HandleFunc("/media/", multipleMiddleware(

22
web_native/build.yaml Normal file
View File

@ -0,0 +1,22 @@
targets:
$default:
sources:
- lib/**
- graphql/**
- schema.graphqls
builders:
artemis:
options:
schema_mapping:
- schema: schema.graphqls
queries_glob: graphql/*.graphql
output: lib/graphql/imgagini_graphql.dart
scalar_mapping:
- graphql_type: Upload
custom_parser_import: 'package:imagini/helpers/upload_serializer.dart'
dart_type:
name: MultipartFile
imports:
- 'package:http/http.dart'
- graphql_type: Time
dart_type: DateTime

View File

@ -0,0 +1,16 @@
mutation createMediaItem($file: Upload!){
createMediaItem(input: {
file: $file
}){
id
createdAt
updatedAt
exifDate
latitude
longitude
isVideo
fileName
origName
userID
}
}

View File

@ -0,0 +1,8 @@
query login($user: String!, $password: String!, $deviceID: ID) {
login(user: $user, password: $password, deviceID: $deviceID) {
result,
device {
id
}
}
}

View File

@ -1,2 +1,3 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@ -1,2 +1,3 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

View File

@ -1,19 +1,25 @@
import 'dart:io';
import 'dart:async';
import 'dart:convert';
import 'package:meta/meta.dart';
import 'package:http/http.dart' as http;
// ignore: uri_does_not_exist
import 'cookie_client_stub.dart'
// ignore: uri_does_not_exist
if (dart.library.html) 'browser_cookie_client.dart'
// ignore: uri_does_not_exist
if (dart.library.io) 'io_cookie_client.dart';
import 'package:imagini/models/api/response/LoginResponse.dart';
import 'package:meta/meta.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:imagini/models/api/response/login_response.dart';
class APIProvider{
String _server, _accessToken, _refreshToken;
static const String _LOGIN_API = "/api/v1/Login";
static const String _MEDIA_ITEMS_API = "/api/v1/MediaItems";
static const String _ALBUMS_API = "/api/v1/Albums";
static const String _TAGS_API = "/api/v1/Tags";
GraphQLClient _client;
HttpLink httpLink;
// CookieLink cookieLink;
static const String _GRAPHQL_ENDPOINT = "/query";
APIProvider({
@required String server,
@ -23,9 +29,27 @@ class APIProvider{
_server = server;
_accessToken = accessToken;
_refreshToken = refreshToken;
httpLink = HttpLink(_server + _GRAPHQL_ENDPOINT,
httpClient: getCookieClient(),
);
// cookieLink = CookieLink(_updateAccessToken, _updateRefreshToken);
_client = GraphQLClient(
cache: GraphQLCache(),
link: httpLink,
);
}
// Endpoint: /api/v1/Login
// void _updateAccessToken(_accessToken) {
// print("Updating Access Token: $_accessToken");
// this._accessToken = _accessToken;
// }
// void _updateRefreshToken(_refreshToken) {
// print("Updating Refresh Token: $_accessToken");
// this._refreshToken = _refreshToken;
// }
Future<LoginResponse> login([
String username,
String password,
@ -34,37 +58,28 @@ class APIProvider{
(username != null && password != null) ||
(_accessToken != null && _refreshToken != null)
);
String loginQuery = """
query login(\$user: String!, \$password: String!, \$deviceID: ID) {
login(user: \$user, password: \$password, deviceID: \$deviceID) {
result,
device {
id
}
}
}""";
http.Response response = await http.post(
Uri.encodeFull(_server + _LOGIN_API),
body: jsonEncode(<String, String>{
'user': username,
'password': password,
}),
).timeout(Duration(seconds: 10));
if (response.statusCode != 200) {
// Fuck
return LoginResponse.fromJson(jsonDecode(response.body));
}
QueryResult response = await _client.query(
QueryOptions(
document: gql(loginQuery),
variables: {
"user": "admin",
"password": "admin"
},
)
);
// Success
String setCookieVal = response.headers["set-cookie"];
List<Cookie> allCookies = setCookieVal.split(',')
.map((cookie) => Cookie.fromSetCookieValue(cookie)).toList();
Cookie accessToken = allCookies.firstWhere((cookie) => cookie.name == "AccessToken");
Cookie refreshToken = allCookies.firstWhere((cookie) => cookie.name == "RefreshToken");
_accessToken = accessToken.toString();
_refreshToken = refreshToken.toString();
print("Status: ${response.statusCode.toString()}");
print("Body: ${response.body.toString()}");
print("AccessToken: ${accessToken.toString()}");
print("RefreshToken: ${refreshToken.toString()}");
return LoginResponse.fromJson(jsonDecode(response.body));
return LoginResponse.fromJson(jsonDecode("{}"));
}
void logout() {

View File

@ -0,0 +1,30 @@
import 'package:http/browser_client.dart';
import "package:http/http.dart";
BaseClient getCookieClient() => ClientWithCookies();
class ClientWithCookies extends BrowserClient {
String _accessToken = "asdasdasd";
String _refreshToken;
@override
Future<StreamedResponse> send(BaseRequest request) async {
request.headers.addAll({
'X-Imagini-AccessToken': _accessToken,
'X-Imagini-RefreshToken': _refreshToken,
});
return super.send(request).then((response) {
if (response.headers.containsKey("x-imagini-accesstoken")) {
this._accessToken = response.headers["x-imagini-accesstoken"];
}
if (response.headers.containsKey("x-imagini-refreshtoken")) {
this._refreshToken = response.headers["x-imagini-refreshtoken"];
}
print("Access Token: $_accessToken");
print("Refresh Token: $_refreshToken");
return response;
});
}
}

View File

@ -0,0 +1,4 @@
import 'package:http/http.dart';
BaseClient getCookieClient() => throw UnsupportedError(
'Cannot create a client without dart:html or dart:io.');

View File

@ -1,5 +1,5 @@
import 'package:imagini/api/APIProvider.dart';
import 'package:imagini/models/api/response/LoginResponse.dart';
import 'package:imagini/api/api_provider.dart';
import 'package:imagini/models/api/response/login_response.dart';
class ImaginiAPIRepository {
APIProvider _apiProvider;

View File

@ -0,0 +1,28 @@
import 'package:http/io_client.dart';
import "package:http/http.dart";
BaseClient getCookieClient() => IOClientWithCookies();
class IOClientWithCookies extends IOClient {
String _accessToken;
String _refreshToken;
@override
Future<IOStreamedResponse> send(BaseRequest request) async {
// String cookie = await getCookie();
// String getCookieString(String _) => cookie;
// request.headers.update('cookie', getCookieString);
return super.send(request).then((response) {
if (response.headers.containsKey("x-imagini-accesstoken")) {
this._accessToken = response.headers["x-imagini-accesstoken"];
}
if (response.headers.containsKey("x-imagini-refreshtoken")) {
this._refreshToken = response.headers["x-imagini-refreshtoken"];
}
print("Access Token: $_accessToken");
print("Refresh Token: $_refreshToken");
return response;
});
}
}

View File

@ -1,9 +1,9 @@
import 'dart:async';
import 'package:imagini/core/ImaginiApplication.dart';
import 'package:imagini/api/ImaginiAPIRepository.dart';
import 'package:imagini/core/imagini_application.dart';
import 'package:imagini/api/imagini_api_repository.dart';
import 'package:imagini/models/api/response/LoginResponse.dart';
import 'package:imagini/models/api/response/login_response.dart';
class SplashBloc{

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:imagini/core/AppComponent.dart';
import 'package:imagini/core/ImaginiApplication.dart';
import 'package:imagini/core/app_component.dart';
import 'package:imagini/core/imagini_application.dart';
class Env {
static Env value;

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:imagini/core/Env.dart';
import 'package:imagini/core/AppProvider.dart';
import 'package:imagini/core/ImaginiApplication.dart';
import 'package:imagini/core/env.dart';
import 'package:imagini/core/app_provider.dart';
import 'package:imagini/core/imagini_application.dart';
class AppComponent extends StatefulWidget {

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';
import 'package:imagini/core/ImaginiApplication.dart';
import 'package:imagini/core/imagini_application.dart';
class AppProvider extends InheritedWidget {

View File

@ -1,9 +1,9 @@
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:imagini/screens/HomeScreen.dart';
import 'package:imagini/screens/LoginScreen.dart';
import 'package:imagini/screens/SplashScreen.dart';
import 'package:imagini/screens/home_screen.dart';
import 'package:imagini/screens/login_screen.dart';
import 'package:imagini/screens/splash_screen.dart';
var splashHandler = new Handler(
handlerFunc: (BuildContext context, Map<String, List<String>> params) {

View File

@ -1,8 +1,8 @@
import 'package:fluro/fluro.dart';
import 'package:imagini/core/AppRoutes.dart';
import 'package:imagini/api/APIProvider.dart';
import 'package:imagini/api/ImaginiAPIRepository.dart';
import 'package:imagini/core/app_routes.dart';
import 'package:imagini/api/api_provider.dart';
import 'package:imagini/api/imagini_api_repository.dart';
class ImaginiApplication {
FluroRouter router;
@ -21,7 +21,7 @@ class ImaginiApplication {
}
_initAPIRepository() {
APIProvider apiProvider = new APIProvider(server: "http://10.0.20.170:8484");
APIProvider apiProvider = new APIProvider(server: "http://localhost:8484");
imaginiAPI = ImaginiAPIRepository(apiProvider);
}
}

View File

@ -0,0 +1,2 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
export 'imgagini_graphql.graphql.dart';

View File

@ -0,0 +1,354 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
import 'package:meta/meta.dart';
import 'package:artemis/artemis.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:equatable/equatable.dart';
import 'package:gql/ast.dart';
import 'package:http/http.dart';
import 'package:imagini/helpers/upload_serializer.dart';
part 'imgagini_graphql.graphql.g.dart';
@JsonSerializable(explicitToJson: true)
class Login$Query$AuthResponse$Device with EquatableMixin {
Login$Query$AuthResponse$Device();
factory Login$Query$AuthResponse$Device.fromJson(Map<String, dynamic> json) =>
_$Login$Query$AuthResponse$DeviceFromJson(json);
String id;
@override
List<Object> get props => [id];
Map<String, dynamic> toJson() =>
_$Login$Query$AuthResponse$DeviceToJson(this);
}
@JsonSerializable(explicitToJson: true)
class Login$Query$AuthResponse with EquatableMixin {
Login$Query$AuthResponse();
factory Login$Query$AuthResponse.fromJson(Map<String, dynamic> json) =>
_$Login$Query$AuthResponseFromJson(json);
@JsonKey(unknownEnumValue: AuthResult.artemisUnknown)
AuthResult result;
Login$Query$AuthResponse$Device device;
@override
List<Object> get props => [result, device];
Map<String, dynamic> toJson() => _$Login$Query$AuthResponseToJson(this);
}
@JsonSerializable(explicitToJson: true)
class Login$Query with EquatableMixin {
Login$Query();
factory Login$Query.fromJson(Map<String, dynamic> json) =>
_$Login$QueryFromJson(json);
Login$Query$AuthResponse login;
@override
List<Object> get props => [login];
Map<String, dynamic> toJson() => _$Login$QueryToJson(this);
}
@JsonSerializable(explicitToJson: true)
class CreateMediaItem$Mutation$MediaItem with EquatableMixin {
CreateMediaItem$Mutation$MediaItem();
factory CreateMediaItem$Mutation$MediaItem.fromJson(
Map<String, dynamic> json) =>
_$CreateMediaItem$Mutation$MediaItemFromJson(json);
String id;
DateTime createdAt;
DateTime updatedAt;
DateTime exifDate;
double latitude;
double longitude;
bool isVideo;
String fileName;
String origName;
String userID;
@override
List<Object> get props => [
id,
createdAt,
updatedAt,
exifDate,
latitude,
longitude,
isVideo,
fileName,
origName,
userID
];
Map<String, dynamic> toJson() =>
_$CreateMediaItem$Mutation$MediaItemToJson(this);
}
@JsonSerializable(explicitToJson: true)
class CreateMediaItem$Mutation with EquatableMixin {
CreateMediaItem$Mutation();
factory CreateMediaItem$Mutation.fromJson(Map<String, dynamic> json) =>
_$CreateMediaItem$MutationFromJson(json);
CreateMediaItem$Mutation$MediaItem createMediaItem;
@override
List<Object> get props => [createMediaItem];
Map<String, dynamic> toJson() => _$CreateMediaItem$MutationToJson(this);
}
enum AuthResult {
@JsonValue('Success')
success,
@JsonValue('Failure')
failure,
@JsonValue('ARTEMIS_UNKNOWN')
artemisUnknown,
}
@JsonSerializable(explicitToJson: true)
class LoginArguments extends JsonSerializable with EquatableMixin {
LoginArguments({@required this.user, @required this.password, this.deviceID});
@override
factory LoginArguments.fromJson(Map<String, dynamic> json) =>
_$LoginArgumentsFromJson(json);
final String user;
final String password;
final String deviceID;
@override
List<Object> get props => [user, password, deviceID];
@override
Map<String, dynamic> toJson() => _$LoginArgumentsToJson(this);
}
class LoginQuery extends GraphQLQuery<Login$Query, LoginArguments> {
LoginQuery({this.variables});
@override
final DocumentNode document = DocumentNode(definitions: [
OperationDefinitionNode(
type: OperationType.query,
name: NameNode(value: 'login'),
variableDefinitions: [
VariableDefinitionNode(
variable: VariableNode(name: NameNode(value: 'user')),
type: NamedTypeNode(
name: NameNode(value: 'String'), isNonNull: true),
defaultValue: DefaultValueNode(value: null),
directives: []),
VariableDefinitionNode(
variable: VariableNode(name: NameNode(value: 'password')),
type: NamedTypeNode(
name: NameNode(value: 'String'), isNonNull: true),
defaultValue: DefaultValueNode(value: null),
directives: []),
VariableDefinitionNode(
variable: VariableNode(name: NameNode(value: 'deviceID')),
type:
NamedTypeNode(name: NameNode(value: 'ID'), isNonNull: false),
defaultValue: DefaultValueNode(value: null),
directives: [])
],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'login'),
alias: null,
arguments: [
ArgumentNode(
name: NameNode(value: 'user'),
value: VariableNode(name: NameNode(value: 'user'))),
ArgumentNode(
name: NameNode(value: 'password'),
value: VariableNode(name: NameNode(value: 'password'))),
ArgumentNode(
name: NameNode(value: 'deviceID'),
value: VariableNode(name: NameNode(value: 'deviceID')))
],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'result'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'device'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'id'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
]))
]))
]))
]);
@override
final String operationName = 'login';
@override
final LoginArguments variables;
@override
List<Object> get props => [document, operationName, variables];
@override
Login$Query parse(Map<String, dynamic> json) => Login$Query.fromJson(json);
}
@JsonSerializable(explicitToJson: true)
class CreateMediaItemArguments extends JsonSerializable with EquatableMixin {
CreateMediaItemArguments({@required this.file});
@override
factory CreateMediaItemArguments.fromJson(Map<String, dynamic> json) =>
_$CreateMediaItemArgumentsFromJson(json);
@JsonKey(
fromJson: fromGraphQLUploadToDartMultipartFile,
toJson: fromDartMultipartFileToGraphQLUpload)
final MultipartFile file;
@override
List<Object> get props => [file];
@override
Map<String, dynamic> toJson() => _$CreateMediaItemArgumentsToJson(this);
}
class CreateMediaItemMutation
extends GraphQLQuery<CreateMediaItem$Mutation, CreateMediaItemArguments> {
CreateMediaItemMutation({this.variables});
@override
final DocumentNode document = DocumentNode(definitions: [
OperationDefinitionNode(
type: OperationType.mutation,
name: NameNode(value: 'createMediaItem'),
variableDefinitions: [
VariableDefinitionNode(
variable: VariableNode(name: NameNode(value: 'file')),
type: NamedTypeNode(
name: NameNode(value: 'Upload'), isNonNull: true),
defaultValue: DefaultValueNode(value: null),
directives: [])
],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'createMediaItem'),
alias: null,
arguments: [
ArgumentNode(
name: NameNode(value: 'input'),
value: ObjectValueNode(fields: [
ObjectFieldNode(
name: NameNode(value: 'file'),
value: VariableNode(name: NameNode(value: 'file')))
]))
],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'id'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'createdAt'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'updatedAt'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'exifDate'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'latitude'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'longitude'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'isVideo'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'fileName'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'origName'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'userID'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
]))
]))
]);
@override
final String operationName = 'createMediaItem';
@override
final CreateMediaItemArguments variables;
@override
List<Object> get props => [document, operationName, variables];
@override
CreateMediaItem$Mutation parse(Map<String, dynamic> json) =>
CreateMediaItem$Mutation.fromJson(json);
}

View File

@ -0,0 +1,166 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'imgagini_graphql.graphql.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Login$Query$AuthResponse$Device _$Login$Query$AuthResponse$DeviceFromJson(
Map<String, dynamic> json) {
return Login$Query$AuthResponse$Device()..id = json['id'] as String;
}
Map<String, dynamic> _$Login$Query$AuthResponse$DeviceToJson(
Login$Query$AuthResponse$Device instance) =>
<String, dynamic>{
'id': instance.id,
};
Login$Query$AuthResponse _$Login$Query$AuthResponseFromJson(
Map<String, dynamic> json) {
return Login$Query$AuthResponse()
..result = _$enumDecodeNullable(_$AuthResultEnumMap, json['result'],
unknownValue: AuthResult.artemisUnknown)
..device = json['device'] == null
? null
: Login$Query$AuthResponse$Device.fromJson(
json['device'] as Map<String, dynamic>);
}
Map<String, dynamic> _$Login$Query$AuthResponseToJson(
Login$Query$AuthResponse instance) =>
<String, dynamic>{
'result': _$AuthResultEnumMap[instance.result],
'device': instance.device?.toJson(),
};
T _$enumDecode<T>(
Map<T, dynamic> enumValues,
dynamic source, {
T unknownValue,
}) {
if (source == null) {
throw ArgumentError('A value must be provided. Supported values: '
'${enumValues.values.join(', ')}');
}
final value = enumValues.entries
.singleWhere((e) => e.value == source, orElse: () => null)
?.key;
if (value == null && unknownValue == null) {
throw ArgumentError('`$source` is not one of the supported values: '
'${enumValues.values.join(', ')}');
}
return value ?? unknownValue;
}
T _$enumDecodeNullable<T>(
Map<T, dynamic> enumValues,
dynamic source, {
T unknownValue,
}) {
if (source == null) {
return null;
}
return _$enumDecode<T>(enumValues, source, unknownValue: unknownValue);
}
const _$AuthResultEnumMap = {
AuthResult.success: 'Success',
AuthResult.failure: 'Failure',
AuthResult.artemisUnknown: 'ARTEMIS_UNKNOWN',
};
Login$Query _$Login$QueryFromJson(Map<String, dynamic> json) {
return Login$Query()
..login = json['login'] == null
? null
: Login$Query$AuthResponse.fromJson(
json['login'] as Map<String, dynamic>);
}
Map<String, dynamic> _$Login$QueryToJson(Login$Query instance) =>
<String, dynamic>{
'login': instance.login?.toJson(),
};
CreateMediaItem$Mutation$MediaItem _$CreateMediaItem$Mutation$MediaItemFromJson(
Map<String, dynamic> json) {
return CreateMediaItem$Mutation$MediaItem()
..id = json['id'] as String
..createdAt = json['createdAt'] == null
? null
: DateTime.parse(json['createdAt'] as String)
..updatedAt = json['updatedAt'] == null
? null
: DateTime.parse(json['updatedAt'] as String)
..exifDate = json['exifDate'] == null
? null
: DateTime.parse(json['exifDate'] as String)
..latitude = (json['latitude'] as num)?.toDouble()
..longitude = (json['longitude'] as num)?.toDouble()
..isVideo = json['isVideo'] as bool
..fileName = json['fileName'] as String
..origName = json['origName'] as String
..userID = json['userID'] as String;
}
Map<String, dynamic> _$CreateMediaItem$Mutation$MediaItemToJson(
CreateMediaItem$Mutation$MediaItem instance) =>
<String, dynamic>{
'id': instance.id,
'createdAt': instance.createdAt?.toIso8601String(),
'updatedAt': instance.updatedAt?.toIso8601String(),
'exifDate': instance.exifDate?.toIso8601String(),
'latitude': instance.latitude,
'longitude': instance.longitude,
'isVideo': instance.isVideo,
'fileName': instance.fileName,
'origName': instance.origName,
'userID': instance.userID,
};
CreateMediaItem$Mutation _$CreateMediaItem$MutationFromJson(
Map<String, dynamic> json) {
return CreateMediaItem$Mutation()
..createMediaItem = json['createMediaItem'] == null
? null
: CreateMediaItem$Mutation$MediaItem.fromJson(
json['createMediaItem'] as Map<String, dynamic>);
}
Map<String, dynamic> _$CreateMediaItem$MutationToJson(
CreateMediaItem$Mutation instance) =>
<String, dynamic>{
'createMediaItem': instance.createMediaItem?.toJson(),
};
LoginArguments _$LoginArgumentsFromJson(Map<String, dynamic> json) {
return LoginArguments(
user: json['user'] as String,
password: json['password'] as String,
deviceID: json['deviceID'] as String,
);
}
Map<String, dynamic> _$LoginArgumentsToJson(LoginArguments instance) =>
<String, dynamic>{
'user': instance.user,
'password': instance.password,
'deviceID': instance.deviceID,
};
CreateMediaItemArguments _$CreateMediaItemArgumentsFromJson(
Map<String, dynamic> json) {
return CreateMediaItemArguments(
file: fromGraphQLUploadToDartMultipartFile(json['file'] as MultipartFile),
);
}
Map<String, dynamic> _$CreateMediaItemArgumentsToJson(
CreateMediaItemArguments instance) =>
<String, dynamic>{
'file': fromDartMultipartFileToGraphQLUpload(instance.file),
};

View File

@ -0,0 +1,4 @@
import 'package:http/http.dart';
MultipartFile fromGraphQLUploadToDartMultipartFile(MultipartFile file) => file;
MultipartFile fromDartMultipartFileToGraphQLUpload(MultipartFile file) => file;

View File

@ -1,4 +1,4 @@
import 'package:imagini/core/Env.dart';
import 'package:imagini/core/env.dart';
void main() => ProductionImagini();

View File

@ -1,3 +0,0 @@
library imagini_api;
export 'src/imagini_api.dart';

View File

@ -1,136 +0,0 @@
import 'dart:io';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:meta/meta.dart';
enum ImaginiState { unknown, authenticated, unauthenticated }
class ImaginiAPI{
String _server, _accessToken, _refreshToken;
final _controller = StreamController<ImaginiState>();
ImaginiAPI({
@required String server,
String accessToken,
String refreshToken
}) {
_server = server;
_accessToken = accessToken;
_refreshToken = refreshToken;
}
Stream<ImaginiState> get status async* {
await Future<void>.delayed(const Duration(seconds: 1));
yield ImaginiState.unauthenticated;
yield* _controller.stream;
}
// Endpoint: /api/v1/Login
Future<void> login([
String username,
String password,
]) async {
assert(
(username != null && password != null) ||
(_accessToken != null && _refreshToken != null)
);
http.Response response = await http.post(
Uri.encodeFull(_server + "/api/v1/Login"),
body: jsonEncode(<String, String>{
'user': username,
'password': password,
}),
).timeout(Duration(seconds: 10));
if (response.statusCode != 200) {
// Fuck
return;
}
// Success
String setCookieVal = response.headers["set-cookie"];
List<Cookie> allCookies = setCookieVal.split(',')
.map((cookie) => Cookie.fromSetCookieValue(cookie)).toList();
Cookie accessToken = allCookies.firstWhere((cookie) => cookie.name == "AccessToken");
Cookie refreshToken = allCookies.firstWhere((cookie) => cookie.name == "RefreshToken");
_accessToken = accessToken.toString();
_refreshToken = refreshToken.toString();
_controller.add(ImaginiState.authenticated);
print("Status: ${response.statusCode.toString()}");
print("Body: ${response.body.toString()}");
print("AccessToken: ${accessToken.toString()}");
print("RefreshToken: ${refreshToken.toString()}");
}
void logout() {
_controller.add(ImaginiState.unauthenticated);
}
Future<String> mediaItems([
String startDate,
String endDate,
String albumID,
List<String> tagID,
String type, // TODO: Make enum
int page,
]) async {
// Query:
// /api/v1/MediaItems
// Derive Params:
// startDate:
// &createdAt=>2020-10-10T10:10:10
// endDate:
// &createdAt=<2020-10-10T10:10:10
// albumID:
// &albumID=9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d
// tagID:
// &tagID=9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d,9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d
// type:
// &type=Photos
// &type=Videos
// page:
// &page=4
// Returns:
// {
//
// }
return null;
}
Future<String> tags([
int page
]) async {
// Query:
// /api/v1/Tags
// Derive Params:
// page:
// &page=4
return null;
}
Future<String> albums([
int page
]) async {
// Query:
// /api/v1/Albums
// Derive Params:
// page:
// &page=4
return null;
}
Future<String> me() async {
return null;
}
void dispose() => _controller.close();
}

View File

@ -1,9 +0,0 @@
name: imagini_api
description: Dart package that implements the Imagini API
version: 1.0.0
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
meta: ^1.1.8

View File

@ -7,14 +7,14 @@ packages:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "12.0.0"
version: "14.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "0.40.6"
version: "0.41.2"
archive:
dependency: transitive
description:
@ -29,6 +29,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.0"
artemis:
dependency: "direct main"
description:
name: artemis
url: "https://pub.dartlang.org"
source: hosted
version: "6.18.3"
async:
dependency: transitive
description:
@ -50,6 +57,62 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.3"
build:
dependency: transitive
description:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.2"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.5"
build_daemon:
dependency: transitive
description:
name: build_daemon
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.7"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.3"
build_runner:
dependency: "direct dev"
description:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.7"
built_collection:
dependency: transitive
description:
name: built_collection
url: "https://pub.dartlang.org"
source: hosted
version: "5.0.0"
built_value:
dependency: transitive
description:
name: built_value
url: "https://pub.dartlang.org"
source: hosted
version: "8.0.0-nullsafety.0"
characters:
dependency: transitive
description:
@ -64,6 +127,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0-nullsafety.3"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
cli_util:
dependency: transitive
description:
@ -78,6 +148,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0-nullsafety.3"
code_builder:
dependency: transitive
description:
name: code_builder
url: "https://pub.dartlang.org"
source: hosted
version: "3.6.0"
collection:
dependency: transitive
description:
@ -85,6 +162,34 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0-nullsafety.5"
connectivity:
dependency: transitive
description:
name: connectivity
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
connectivity_for_web:
dependency: transitive
description:
name: connectivity_for_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.1+4"
connectivity_macos:
dependency: transitive
description:
name: connectivity_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0+7"
connectivity_platform_interface:
dependency: transitive
description:
name: connectivity_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.6"
convert:
dependency: transitive
description:
@ -92,13 +197,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
coverage:
dependency: transitive
description:
name: coverage
url: "https://pub.dartlang.org"
source: hosted
version: "0.14.2"
crypto:
dependency: transitive
description:
@ -113,13 +211,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.12"
equatable:
dependency: "direct main"
description:
name: equatable
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.5"
version: "1.2.6"
fake_async:
dependency: transitive
description:
@ -147,7 +252,14 @@ packages:
name: file_picker
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.5"
version: "2.1.6"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
fluro:
dependency: "direct main"
description:
@ -166,7 +278,7 @@ packages:
name: flutter_bloc
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.1"
version: "6.1.2"
flutter_driver:
dependency: transitive
description: flutter
@ -178,7 +290,7 @@ packages:
name: flutter_platform_widgets
url: "https://pub.dartlang.org"
source: hosted
version: "0.72.0"
version: "0.80.0-beta.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@ -199,7 +311,7 @@ packages:
name: flutter_staggered_grid_view
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.3"
version: "0.3.4"
flutter_test:
dependency: "direct dev"
description: flutter
@ -222,6 +334,90 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
gql:
dependency: "direct main"
description:
name: gql
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.4"
gql_code_gen:
dependency: transitive
description:
name: gql_code_gen
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
gql_dedupe_link:
dependency: transitive
description:
name: gql_dedupe_link
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.10"
gql_error_link:
dependency: transitive
description:
name: gql_error_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.1-alpha+1601131172858"
gql_exec:
dependency: transitive
description:
name: gql_exec
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.5"
gql_http_link:
dependency: transitive
description:
name: gql_http_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.2"
gql_link:
dependency: transitive
description:
name: gql_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.1"
gql_transform_link:
dependency: transitive
description:
name: gql_transform_link
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5"
graphql:
dependency: transitive
description:
name: graphql
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.1"
graphql_flutter:
dependency: "direct main"
description:
name: graphql_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.1"
graphs:
dependency: transitive
description:
name: graphs
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0"
hive:
dependency: transitive
description:
name: hive
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.4+1"
http:
dependency: "direct main"
description:
@ -229,6 +425,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
http_parser:
dependency: transitive
description:
@ -255,20 +458,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3-nullsafety.3"
json_rpc_2:
dependency: transitive
json_annotation:
dependency: "direct main"
description:
name: json_rpc_2
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.2"
version: "3.1.1"
json_serializable:
dependency: "direct dev"
description:
name: json_serializable
url: "https://pub.dartlang.org"
source: hosted
version: "3.5.1"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "0.11.4"
version: "1.0.0"
matcher:
dependency: transitive
description:
@ -277,12 +487,19 @@ packages:
source: hosted
version: "0.12.10-nullsafety.3"
meta:
dependency: transitive
dependency: "direct main"
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0-nullsafety.6"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
nested:
dependency: transitive
description:
@ -304,6 +521,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
normalize:
dependency: transitive
description:
name: normalize
url: "https://pub.dartlang.org"
source: hosted
version: "0.4.7"
package_config:
dependency: transitive
description:
@ -318,6 +542,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0-nullsafety.3"
path_provider:
dependency: transitive
description:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.27"
path_provider_linux:
dependency: transitive
description:
@ -325,6 +556,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+2"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.4+8"
path_provider_platform_interface:
dependency: transitive
description:
@ -345,7 +583,7 @@ packages:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0-nullsafety.3"
version: "1.10.0"
platform:
dependency: transitive
description:
@ -366,7 +604,7 @@ packages:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.0-nullsafety.3"
version: "1.4.0"
process:
dependency: transitive
description:
@ -388,6 +626,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.4"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.8"
recase:
dependency: transitive
description:
name: recase
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
rxdart:
dependency: transitive
description:
name: rxdart
url: "https://pub.dartlang.org"
source: hosted
version: "0.24.1"
shared_preferences:
dependency: "direct main"
description:
@ -429,26 +688,33 @@ packages:
name: shared_preferences_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2+2"
version: "0.0.2+3"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.9"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.4"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_map_stack_trace:
source_gen:
dependency: transitive
description:
name: source_map_stack_trace
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.4"
source_maps:
dependency: transitive
description:
name: source_maps
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.10-nullsafety.3"
version: "0.9.10+1"
source_span:
dependency: transitive
description:
@ -470,6 +736,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0-nullsafety.3"
stream_transform:
dependency: transitive
description:
name: stream_transform
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
string_scanner:
dependency: transitive
description:
@ -498,13 +771,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.19-nullsafety.6"
test_core:
timing:
dependency: transitive
description:
name: test_core
name: timing
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.12-nullsafety.9"
version: "0.1.1+3"
typed_data:
dependency: transitive
description:
@ -546,7 +819,7 @@ packages:
name: url_launcher_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.5+1"
version: "0.1.5+3"
url_launcher_windows:
dependency: transitive
description:
@ -554,6 +827,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.1+3"
uuid_enhanced:
dependency: transitive
description:
name: uuid_enhanced
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
vector_math:
dependency: transitive
description:
@ -581,7 +861,7 @@ packages:
name: web_socket_channel
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.0"
webdriver:
dependency: transitive
description:
@ -589,13 +869,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
websocket:
dependency: transitive
description:
name: websocket
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.5"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.4"
version: "1.7.4+1"
xdg_directories:
dependency: transitive
description:
@ -612,4 +899,4 @@ packages:
version: "2.2.1"
sdks:
dart: ">=2.12.0-0.0 <3.0.0"
flutter: ">=1.22.0 <2.0.0"
flutter: ">=1.22.0"

View File

@ -23,23 +23,31 @@ environment:
dependencies:
flutter:
sdk: flutter
graphql_flutter: ^4.0.1
flutter_staggered_grid_view: "^0.3.2"
fluro: ^1.7.8
equatable: ^1.2.5
http: ^0.12.2
shared_preferences: ^0.5.12+4
flutter_secure_storage: ^3.3.5
flutter_platform_widgets: ^0.72.0
flutter_platform_widgets: ^0.80.0-beta.0
flutter_bloc: ^6.1.1
file_picker: ^2.1.5
url_launcher: ^5.7.10
cupertino_icons: ^1.0.1
artemis: '>=6.0.0 <7.0.0' # only if you're using ArtemisClient!
json_annotation: ^3.1.0
meta: '>=1.0.0 <2.0.0' # only if you have non nullable fields
gql: '>=0.12.3 <1.0.0'
dev_dependencies:
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
artemis: '>=6.0.0 <7.0.0'
build_runner: ^1.10.4
json_serializable: ^3.5.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

384
web_native/schema.graphqls Normal file
View File

@ -0,0 +1,384 @@
# https://gqlgen.com/reference/scalars/
scalar Time
scalar Upload
# https://gqlgen.com/reference/directives/
directive @hasMinRole(role: Role!) on FIELD_DEFINITION
directive @isPrivate on FIELD_DEFINITION | INPUT_FIELD_DEFINITION
directive @meta(
gorm: String,
) on OBJECT | FIELD_DEFINITION | ENUM_VALUE | INPUT_FIELD_DEFINITION | ENUM | INPUT_OBJECT | ARGUMENT_DEFINITION
enum Role {
Admin
User
}
enum DeviceType {
iOS
Android
Chrome
Firefox
InternetExplorer
Edge
Safari
Unknown
}
enum AuthType {
Local
LDAP
}
enum OrderDirection {
ASC
DESC
}
# ------------------------------------------------------------
# ---------------------- Authentication ----------------------
# ------------------------------------------------------------
enum AuthResult {
Success
Failure
}
type AuthResponse {
result: AuthResult!
device: Device
error: String
}
# ------------------------------------------------------------
# ----------------------- Type Filters -----------------------
# ------------------------------------------------------------
input TimeFilter {
equalTo: Time
notEqualTo: Time
lessThan: Time
lessThanOrEqualTo: Time
greaterThan: Time
greaterThanOrEqualTo: Time
}
input IntFilter {
equalTo: Int
notEqualTo: Int
lessThan: Int
lessThanOrEqualTo: Int
greaterThan: Int
greaterThanOrEqualTo: Int
}
input FloatFilter {
equalTo: Float
notEqualTo: Float
lessThan: Float
lessThanOrEqualTo: Float
greaterThan: Float
greaterThanOrEqualTo: Float
}
input BooleanFilter {
equalTo: Boolean
notEqualTo: Boolean
}
input IDFilter {
equalTo: ID
notEqualTo: ID
}
input StringFilter {
equalTo: String
notEqualTo: String
startsWith: String
notStartsWith: String
endsWith: String
notEndsWith: String
contains: String
notContains: String
}
input RoleFilter {
equalTo: Role
notEqualTo: Role
}
input DeviceTypeFilter {
equalTo: DeviceType
notEqualTo: DeviceType
}
input AuthTypeFilter {
equalTo: AuthType
notEqualTo: AuthType
}
# ------------------------------------------------------------
# -------------------- Object Definitions --------------------
# ------------------------------------------------------------
type User {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
updatedAt: Time
email: String! @meta(gorm: "not null;unique")
username: String! @meta(gorm: "not null;unique")
firstName: String
lastName: String
role: Role! @meta(gorm: "default:User;not null")
authType: AuthType! @meta(gorm: "default:Local;not null")
password: String @isPrivate
devices: [Device!] @meta(gorm: "foreignKey:UserID")
mediaItems: [MediaItem!] @meta(gorm: "foreignKey:UserID")
}
type Device {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
updatedAt: Time
name: String! @meta(gorm: "not null")
type: DeviceType! @meta(gorm: "default:Unknown;not null")
refreshKey: String @isPrivate
userID: ID! @meta(gorm: "not null")
}
type MediaItem {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
updatedAt: Time
exifDate: Time
latitude: Float @meta(gorm: "precision:5")
longitude: Float @meta(gorm: "precision:5")
isVideo: Boolean! @meta(gorm: "default:false;not null")
fileName: String! @meta(gorm: "not null")
origName: String! @meta(gorm: "not null")
tags: [Tag] @meta(gorm: "many2many:media_tags;foreignKey:ID,UserID;References:ID")
albums: [Album] @meta(gorm: "many2many:media_albums;foreignKey:ID,UserID;Refrences:ID")
userID: ID! @meta(gorm: "not null")
}
type Tag {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
updatedAt: Time
name: String! @meta(gorm: "unique;not null")
userID: ID! @meta(gorm: "not null")
}
type Album {
id: ID! @meta(gorm: "primaryKey;not null")
createdAt: Time
updatedAt: Time
name: String! @meta(gorm: "unique;not null")
userID: ID! @meta(gorm: "not null")
}
# ------------------------------------------------------------
# ---------------------- Object Filters ----------------------
# ------------------------------------------------------------
input UserFilter {
id: IDFilter
createdAt: TimeFilter
updatedAt: TimeFilter
username: StringFilter
firstName: StringFilter
lastName: StringFilter
role: RoleFilter
authType: AuthTypeFilter
# and: UserFilter
# or: UserFilter
}
input MediaItemFilter {
id: IDFilter
createdAt: TimeFilter
updatedAt: TimeFilter
exifDate: TimeFilter
latitude: FloatFilter
longitude: FloatFilter
isVideo: BooleanFilter
origName: StringFilter
tags: TagFilter
albums: AlbumFilter
# and: MediaItemFilter
# or: MediaItemFilter
}
input DeviceFilter {
id: IDFilter
createdAt: TimeFilter
updatedAt: TimeFilter
name: StringFilter
type: DeviceTypeFilter
# and: MediaItemFilter
# or: MediaItemFilter
}
input TagFilter {
id: IDFilter
createdAt: TimeFilter
updatedAt: TimeFilter
name: StringFilter
# and: MediaItemFilter
# or: MediaItemFilter
}
input AlbumFilter {
id: IDFilter
createdAt: TimeFilter
updatedAt: TimeFilter
name: StringFilter
# and: MediaItemFilter
# or: MediaItemFilter
}
# ------------------------------------------------------------
# -------------------------- Inputs --------------------------
# ------------------------------------------------------------
input NewUser {
email: String!
username: String!
firstName: String
lastName: String
role: Role!
authType: AuthType!
password: String
}
input NewMediaItem {
file: Upload!
tags: [ID!]
albums: [ID!]
}
input NewTag {
name: String!
}
input NewAlbum {
name: String!
}
input Page {
size: Int
page: Int
}
input Order {
by: String
direction: OrderDirection
}
# ------------------------------------------------------------
# ------------------------ Responses -------------------------
# ------------------------------------------------------------
type PageResponse {
size: Int!
page: Int!
total: Int!
}
type MediaItemResponse {
data: [MediaItem]
page: PageResponse!
}
type UserResponse {
data: [User]
page: PageResponse!
}
type DeviceResponse {
data: [Device]
page: PageResponse!
}
type TagResponse {
data: [Tag]
page: PageResponse!
}
type AlbumResponse {
data: [Album]
page: PageResponse!
}
# ------------------------------------------------------------
# --------------------- Query & Mutations --------------------
# ------------------------------------------------------------
type Query {
# Authentication
login(
user: String!
password: String!
deviceID: ID
): AuthResponse!
logout: AuthResponse! @hasMinRole(role: User)
# Single Item
mediaItem(
id: ID!
): MediaItem! @hasMinRole(role: User)
device(
id: ID!
): Device! @hasMinRole(role: User)
album(
id: ID!
): Album! @hasMinRole(role: User)
user(
id: ID!
): User! @hasMinRole(role: Admin)
tag(
id: ID!
): Tag! @hasMinRole(role: User)
me: User! @hasMinRole(role: User)
# All
mediaItems(
filter: MediaItemFilter
page: Page
order: Order
): MediaItemResponse! @hasMinRole(role: User)
devices(
filter: DeviceFilter
page: Page
order: Order
): DeviceResponse! @hasMinRole(role: User)
albums(
filter: AlbumFilter
page: Page
order: Order
): AlbumResponse! @hasMinRole(role: User)
tags(
filter: TagFilter
page: Page
order: Order
): TagResponse! @hasMinRole(role: User)
users(
filter: UserFilter
page: Page
order: Order
): UserResponse! @hasMinRole(role: Admin)
}
type Mutation {
createMediaItem(input: NewMediaItem!): MediaItem! @hasMinRole(role: User)
createAlbum(input: NewAlbum!): Album! @hasMinRole(role: User)
createTag(input: NewTag!): Tag! @hasMinRole(role: User)
createUser(input: NewUser!): User! @hasMinRole(role: Admin)
}