Skip to content

Commit

Permalink
Merge branch 'fix/add-regional-support'
Browse files Browse the repository at this point in the history
  • Loading branch information
PhiFry committed Oct 4, 2024
2 parents e8da9fc + e1ae020 commit f7d875a
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 15 deletions.
2 changes: 2 additions & 0 deletions code_generator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Usage: flutter_storyblok_code_generator generate [arguments]
-h, --help Print this usage information.
-s, --space_id (mandatory) Your Storyblok Space ID
-p, --personal_access_token (mandatory) Your Personal Access Token, not your Space access token
-l, --space_location The server location of the space
[eu (default), us, ca, ap, cn]
-r, --rate_limit Your rate limit (depending on your plan)
(defaults to "3")
-o, --output_path A directory path where the output file "bloks.generated.dart" will be created
Expand Down
13 changes: 12 additions & 1 deletion code_generator/bin/flutter_storyblok_code_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';

import 'package:args/command_runner.dart';
import 'package:flutter_storyblok/models.dart' as sb;
import 'package:flutter_storyblok_code_generator/flutter_storyblok_code_generator.dart';
import 'package:flutter_storyblok_code_generator/src/utils/utils.dart';

Expand All @@ -26,6 +27,7 @@ void main(List<String> args) async {
class GenerateCommand extends Command {
static const _optionSpaceId = "space_id";
static const _optionPAT = "personal_access_token";
static const _optionLocation = "space_location";
static const _optionRateLimit = "rate_limit";
static const _nameOutputPath = "output_path";
static const _outPutFileName = "bloks.generated.dart";
Expand All @@ -43,6 +45,14 @@ class GenerateCommand extends Command {
help: "Your Personal Access Token, not your Space access token",
mandatory: true,
);
argParser.addOption(
_optionLocation,
abbr: "l",
help: "The server location of the space",
mandatory: false,
allowed: sb.Region.values.map((e) => e.name),
defaultsTo: sb.Region.eu.name,
);
argParser.addOption(
_optionRateLimit,
abbr: "r",
Expand Down Expand Up @@ -73,8 +83,9 @@ class GenerateCommand extends Command {
final pat = results[_optionPAT] as String;
final rateLimit = results[_optionRateLimit] as String;
final outputPath = results[_nameOutputPath] as String;
final region = results[_optionLocation] as String;

final apiClient = StoryblokHttpClient(spaceId, pat, int.parse(rateLimit));
final apiClient = StoryblokHttpClient(spaceId, pat, sb.Region.values.byName(region), int.parse(rateLimit));
final datasourcesWithEntriesFuture = apiClient.getDatasourcesWithEntries();
final componentsFuture = apiClient.getComponents();

Expand Down
30 changes: 28 additions & 2 deletions code_generator/lib/src/http_client.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:convert';

import 'package:flutter_storyblok/models.dart' as sb;
import 'package:flutter_storyblok_code_generator/src/models/component.dart';
import 'package:flutter_storyblok_code_generator/src/models/datasource.dart';
import 'package:flutter_storyblok_code_generator/src/models/datasource_entry.dart';
Expand All @@ -14,11 +15,13 @@ class StoryblokHttpClient {
StoryblokHttpClient(
this.spaceId,
this.authorization,
this.region,
int rateLimit,
) : _rateLimit = _RateLimit(rateLimit);

final String spaceId;
final String authorization;
final sb.Region region;
final _RateLimit _rateLimit;

Future<List<Component>> getComponents() async {
Expand Down Expand Up @@ -58,14 +61,14 @@ class StoryblokHttpClient {
await _rateLimit();

final response = await http.get(
Uri.https("mapi.storyblok.com", "/v1/spaces/$spaceId/$path", params),
region.buildUri(path: "v1/spaces/$spaceId/$path", queryParameters: params),
headers: {"Authorization": authorization},
);
final json = jsonDecode(response.body);
if (json is JSONMap) {
return json;
} else if (json is List) {
throwMessage("Failed to fetch $path make sure space_id is correct. Error body: $json");
throwMessage("Failed to fetch $path make sure space_id and region is correct. Error body: $json");
} else {
throwMessage("Unknown error");
}
Expand Down Expand Up @@ -100,3 +103,26 @@ class _RateLimit {
_currentThrottleCounter += 1;
}
}

extension RegionCodeGen on sb.Region {
String get baseUrl {
switch (this) {
case sb.Region.eu:
return "https://mapi.storyblok.com";
case sb.Region.us:
return "https://api-us.storyblok.com";
case sb.Region.ap:
return "https://api-ap.storyblok.com";
case sb.Region.ca:
return "https://api-ca.storyblok.com";
case sb.Region.cn:
return "https://app.storyblokchina.cn";
default:
return "https://mapi.storyblok.com";
}
}

Uri buildUri({required String path, JSONMap? queryParameters}) {
return Uri.parse("$baseUrl/$path").replace(queryParameters: queryParameters);
}
}
59 changes: 59 additions & 0 deletions code_generator/test/regions_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'package:flutter_storyblok/models.dart' as sb;
import 'package:flutter_storyblok_code_generator/src/http_client.dart';
import 'package:test/test.dart';

void main() {
test('Test region url build', () {
sb.Region.values.forEach(_expectRegion);
});
}

void _expectRegion(sb.Region region) {
switch (region) {
case sb.Region.eu:
expect(
region.buildUri(path: "somePath"),
Uri.parse("https://mapi.storyblok.com/somePath"),
);
expect(
region.buildUri(path: "somePath", queryParameters: {"foo": "bar"}),
Uri.parse("https://mapi.storyblok.com/somePath?foo=bar"),
);
case sb.Region.us:
expect(
region.buildUri(path: "somePath"),
Uri.parse("https://api-us.storyblok.com/somePath"),
);
expect(
region.buildUri(path: "somePath", queryParameters: {"foo": "bar"}),
Uri.parse("https://api-us.storyblok.com/somePath?foo=bar"),
);
case sb.Region.ca:
expect(
region.buildUri(path: "somePath"),
Uri.parse("https://api-ca.storyblok.com/somePath"),
);
expect(
region.buildUri(path: "somePath", queryParameters: {"foo": "bar"}),
Uri.parse("https://api-ca.storyblok.com/somePath?foo=bar"),
);
case sb.Region.ap:
expect(
region.buildUri(path: "somePath"),
Uri.parse("https://api-ap.storyblok.com/somePath"),
);
expect(
region.buildUri(path: "somePath", queryParameters: {"foo": "bar"}),
Uri.parse("https://api-ap.storyblok.com/somePath?foo=bar"),
);
case sb.Region.cn:
expect(
region.buildUri(path: "somePath"),
Uri.parse("https://app.storyblokchina.cn/somePath"),
);
expect(
region.buildUri(path: "somePath", queryParameters: {"foo": "bar"}),
Uri.parse("https://app.storyblokchina.cn/somePath?foo=bar"),
);
}
}
1 change: 1 addition & 0 deletions lib/models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export 'src/models/pagination.dart';
export 'src/models/resolve_links.dart';
export 'src/models/story_identifier.dart';
export 'src/models/story.dart';
export 'src/models/region.dart';
7 changes: 7 additions & 0 deletions lib/src/models/region.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
enum Region {
eu,
us,
ca,
ap,
cn;
}
46 changes: 34 additions & 12 deletions lib/src/storyblok_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import 'package:http/http.dart' as http;

/// Used to fetch content from the Storyblok Content Delivery API
final class StoryblokClient<StoryContent> {
static const _apiHost = "api.storyblok.com";

static const _pathStories = "v2/cdn/stories";
static const _pathDatasources = "v2/cdn/datasources";
static const _pathDatasourceEntries = "v2/cdn/datasource_entries";
static const _pathLinks = "v2/cdn/links";
static const _pathTags = "v2/cdn/tags";
static const _pathStories = "stories";
static const _pathDatasources = "datasources";
static const _pathDatasourceEntries = "datasource_entries";
static const _pathLinks = "links";
static const _pathTags = "tags";

StoryblokClient({
Region region = Region.eu,
required String accessToken,
ContentVersion? version,
bool useCacheInvalidation = true,
Expand All @@ -24,8 +23,10 @@ final class StoryblokClient<StoryContent> {
},
_version = version,
_useCacheInvalidation = useCacheInvalidation,
_storyContentBuilder = storyContentBuilder;
_storyContentBuilder = storyContentBuilder,
_region = region;

final Region _region;
final ContentVersion? _version;
final Map<String, String> _baseParameters;
final bool _useCacheInvalidation;
Expand Down Expand Up @@ -274,11 +275,11 @@ final class StoryblokClient<StoryContent> {
Map<String, String>? queryParameters,
}) async {
final cacheVersion = _cacheVersion;

// TODO: Rate limit https://www.storyblok.com/docs/api/content-delivery/v2/getting-started/rate-limit
final uri = Uri.https(
_apiHost,
path,
{
final uri = _region.buildUri(
path: path,
queryParameters: {
..._baseParameters,
if (queryParameters != null) ...queryParameters,
},
Expand Down Expand Up @@ -312,3 +313,24 @@ extension _DateTimeFormat on DateTime {
return "$year-$month-$day $hour:$minute";
}
}

extension RegionUrl on Region {
String get baseUrl {
switch (this) {
case Region.eu:
return "https://api.storyblok.com/v2/cdn";
case Region.us:
return "https://api-us.storyblok.com/v2/cdn";
case Region.ca:
return "https://api-ca.storyblok.com/v2/cdn";
case Region.ap:
return "https://api-ap.storyblok.com/v2/cdn";
case Region.cn:
return "https://app.storyblokchina.cn";
}
}

Uri buildUri({required String path, Map<String, String>? queryParameters}) {
return Uri.parse("$baseUrl/$path").replace(queryParameters: queryParameters);
}
}
59 changes: 59 additions & 0 deletions test/regions_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import 'package:flutter_storyblok/models.dart';
import 'package:flutter_storyblok/src/storyblok_client.dart';
import 'package:test/test.dart';

void main() {
test('Test region url build', () {
Region.values.forEach(_expectRegion);
});
}

void _expectRegion(Region region) {
switch (region) {
case Region.eu:
expect(
region.buildUri(path: "somePath"),
Uri.parse("https://api.storyblok.com/v2/cdn/somePath"),
);
expect(
region.buildUri(path: "somePath", queryParameters: {"foo": "bar"}),
Uri.parse("https://api.storyblok.com/v2/cdn/somePath?foo=bar"),
);
case Region.us:
expect(
region.buildUri(path: "somePath"),
Uri.parse("https://api-us.storyblok.com/v2/cdn/somePath"),
);
expect(
region.buildUri(path: "somePath", queryParameters: {"foo": "bar"}),
Uri.parse("https://api-us.storyblok.com/v2/cdn/somePath?foo=bar"),
);
case Region.ca:
expect(
region.buildUri(path: "somePath"),
Uri.parse("https://api-ca.storyblok.com/v2/cdn/somePath"),
);
expect(
region.buildUri(path: "somePath", queryParameters: {"foo": "bar"}),
Uri.parse("https://api-ca.storyblok.com/v2/cdn/somePath?foo=bar"),
);
case Region.ap:
expect(
region.buildUri(path: "somePath"),
Uri.parse("https://api-ap.storyblok.com/v2/cdn/somePath"),
);
expect(
region.buildUri(path: "somePath", queryParameters: {"foo": "bar"}),
Uri.parse("https://api-ap.storyblok.com/v2/cdn/somePath?foo=bar"),
);
case Region.cn:
expect(
region.buildUri(path: "somePath"),
Uri.parse("https://app.storyblokchina.cn/somePath"),
);
expect(
region.buildUri(path: "somePath", queryParameters: {"foo": "bar"}),
Uri.parse("https://app.storyblokchina.cn/somePath?foo=bar"),
);
}
}

0 comments on commit f7d875a

Please sign in to comment.